C++中fork()父子进程同地址异值原因及无文件同步方法咨询
关于fork()机制的两个问题解答
问题1:父子进程变量虚拟地址相同但值不同的原因
这是虚拟内存和**写时复制(Copy-On-Write, COW)**机制共同作用的结果:
- 现代操作系统给每个进程分配独立的虚拟地址空间,进程内看到的内存地址都是虚拟地址,而非物理内存的真实地址。fork()创建子进程时,会复制父进程的虚拟地址空间映射表,但不会立刻复制物理内存页。
- 初始时父子进程的虚拟地址会映射到同一块物理内存页,读操作共享该物理页,但当任何一方尝试修改内存内容时,操作系统会为修改的进程复制一份新的物理页,修改只作用在新页上,另一进程的映射仍指向原来的物理页。
- 你的代码中,fork()后子进程修改
*a触发了写时复制,子进程获得独立的物理页;父进程的物理页还是初始值5,因此父进程执行*a +=2后得到7,子进程得到6。而两者的虚拟地址a完全相同——因为虚拟地址空间各自独立,相同的虚拟地址对应不同的物理页,自然值不一样。
你的预期错误在于误以为fork()后父子进程共享可写内存,实际上默认情况下只有读操作共享,写操作会触发内存复制,父子内存空间是完全独立的。
问题2:不使用文件实现父子进程修改同步的方法
可以用以下几种进程间通信(IPC)机制:
- 共享内存:
这是最直接的方式,让父子进程映射同一块物理内存到各自的虚拟地址空间。可以用shmget()/shmat()系统调用,或者mmap()创建匿名共享内存(父子进程会继承mmap的映射)。修改共享内存时需要搭配同步机制(比如信号量)防止竞态条件。 - 匿名管道:
fork()创建的子进程会继承父进程的文件描述符,父进程可先通过pipe()创建匿名管道,父子进程通过管道互相发送修改后的数据实现同步。注意匿名管道是半双工,需要双向同步的话要创建两个管道。 - 信号量:
单独的信号量无法传递数据,但可以配合共享内存使用,用来同步父子进程的读写操作,避免出现一个进程写时另一个进程读的冲突。 - 消息队列:
用msgget()/msgsnd()/msgrcv()创建消息队列,父子进程可以通过队列发送包含变量值的消息,实现修改同步。
示例:用mmap实现共享内存同步
#include <bits/stdc++.h> #include <unistd.h> #include <sys/mman.h> #include <sys/wait.h> using namespace std; int main() { // 创建匿名共享内存,大小为int,权限可读可写,共享属性,匿名 int *a = (int*)mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); *a = 5; cout << a << endl; int pid = fork(); if (pid != 0) { sleep(2); *a += 2; cout << a << " " << *a << " PARENT" << endl; wait(NULL); // 等待子进程结束 munmap(a, sizeof(int)); } else { *a += 1; cout << a << " " << *a << " CHILD" << endl; munmap(a, sizeof(int)); } return 0; }
这段代码用MAP_SHARED属性的mmap创建共享内存,fork后父子进程修改的是同一块物理内存,输出会符合你预期的0x... 6 CHILD和0x... 8 PARENT。
内容的提问来源于stack exchange,提问作者raj
相关产品推荐
相关产品推荐

