子进程调用exec()后,父子进程如何共享内存?能否使用mmap?
Great question—this is a common pitfall when working with fork() and exec() together. Let's break down why your original mmap shared memory stops working, and how to fix it (including using mmap correctly!).
Why the original mmap shared memory fails after exec()
When a child process calls exec(), it completely replaces its entire user-space address space: the code, data, heap, and stack are all swapped out for the new program's contents. Any existing memory mappings (including your original mmap shared region) are discarded in this process—so the exec'd program has no way to access the old shared memory.
Can we still use mmap to share memory? Yes—here's how
You absolutely can use mmap for shared memory across exec(), but you need to adjust your approach to account for the address space replacement. The key idea is to create a shared memory source that the exec'd program can explicitly re-map after it starts up. Here are two reliable methods:
1. Use a file-backed mmap (with a persistent file or POSIX shared memory object)
Instead of using anonymous mmap (MAP_ANONYMOUS), create a shared memory region backed by a file (or a POSIX shared memory object, which acts like a file but lives in /dev/shm for faster access). Then:
- The parent process creates and maps this file/shared object first.
- When forking the child, pass the file descriptor or the path to the shared object to the exec'd program (via command-line arguments, inherited file descriptors, or environment variables).
- The exec'd program then opens the file/shared object and calls
mmap()on it to connect to the same shared memory region as the parent.
Example code snippets:
Parent process:
#include <stdio.h> #include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/wait.h> int main() { // Create a POSIX shared memory object int shm_fd = shm_open("/my_shared_region", O_CREAT | O_RDWR, 0666); if (shm_fd == -1) { perror("shm_open failed"); return 1; } // Set the size of the shared region if (ftruncate(shm_fd, 4096) == -1) { perror("ftruncate failed"); close(shm_fd); shm_unlink("/my_shared_region"); return 1; } // Map the shared region into parent's address space void *shared_ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); if (shared_ptr == MAP_FAILED) { perror("mmap failed"); close(shm_fd); shm_unlink("/my_shared_region"); return 1; } // Write initial data to shared memory sprintf(shared_ptr, "Hello from parent!"); // Fork child process pid_t pid = fork(); if (pid == 0) { // Pass the shared memory file descriptor to the exec'd program as an argument char fd_str[10]; snprintf(fd_str, sizeof(fd_str), "%d", shm_fd); execl("./exec_child", "./exec_child", fd_str, NULL); // If execl returns, it failed perror("execl failed"); return 1; } else if (pid > 0) { // Wait for child to finish wait(NULL); // Read the updated data from shared memory printf("Parent received: %s\n", (char*)shared_ptr); // Cleanup munmap(shared_ptr, 4096); close(shm_fd); shm_unlink("/my_shared_region"); } else { perror("fork failed"); close(shm_fd); shm_unlink("/my_shared_region"); return 1; } return 0; }
Exec'd child program (exec_child.c):
#include <stdio.h> #include <sys/mman.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "Usage: %s <shared_memory_fd>\n", argv[0]); return 1; } // Convert the passed file descriptor string to an int int shm_fd = atoi(argv[1]); // Map the shared region into this process's address space void *shared_ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); if (shared_ptr == MAP_FAILED) { perror("mmap failed"); close(shm_fd); return 1; } // Read and modify the shared data printf("Exec'd child received: %s\n", (char*)shared_ptr); sprintf(shared_ptr, "Hello from exec'd child!"); // Cleanup munmap(shared_ptr, 4096); close(shm_fd); return 0; }
2. Inherit file descriptors across exec()
By default, file descriptors that are not marked as FD_CLOEXEC are inherited by the exec'd program. So instead of passing the FD as a command-line argument, you can just ensure the shared memory file descriptor isn't closed on exec (which is the default behavior unless you set FD_CLOEXEC with fcntl()). The exec'd program can then use that FD directly to mmap the shared region.
Alternative approaches
If you don't want to use mmap directly, you can also use System V shared memory (shmget(), shmat()), but this is an older API with more cumbersome setup. POSIX shared memory + mmap is generally preferred for its simplicity and compatibility with modern systems.
内容的提问来源于stack exchange,提问作者Daniel Ortega

