gma*_*man 11 c operating-system fork
我引用"当进程使用fork()调用创建一个新进程时,只有父进程和新分叉的子进程之间共享共享内存段.堆栈和堆的副本是为新创建的进程"从Silberschatz的"操作系统概念"解决方案.
但是当我尝试这个程序的时候
#include <stdio.h>
#include <sys/types.h>
#define MAX_COUNT 200
void ChildProcess(void); /* child process prototype */
void ParentProcess(void); /* parent process prototype */
void main(void)
{
pid_t pid;
char * x=(char *)malloc(10);
pid = fork();
if (pid == 0)
ChildProcess();
else
ParentProcess();
printf("the address is %p\n",x);
}
void ChildProcess(void)
{
printf(" *** Child process ***\n");
}
void ParentProcess(void)
{
printf("*** Parent*****\n");
}
Run Code Online (Sandbox Code Playgroud)
结果如下:
*** Parent*****
the address is 0x1370010
*** Child process ***
the address is 0x1370010
Run Code Online (Sandbox Code Playgroud)
父和子两者都打印堆中的相同地址.
谁能解释我这里的矛盾.请清楚说明父母和孩子在内存空间中共享的所有内容.
Pat*_*ard 27
从另一个线程引用自己.
发出fork()系统调用时,会创建与父进程对应的所有页面的副本,由OS进行子进程加载到单独的内存位置.但在某些情况下不需要这样做.考虑一个子进行"exec"系统调用或在fork()之后很快退出的情况.当需要子进程来执行父进程的命令时,不需要复制父进程的页面,因为exec用要执行的命令替换调用它的进程的地址空间.
在这种情况下,使用称为写时复制(COW)的技术.使用此技术,当发生fork时,不会为子进程复制父进程的页面.相反,页面在子进程和父进程之间共享.每当进程(父进程或子进程)修改页面时,就会对执行修改的进程(父进程或子进程)单独创建该特定页面的单独副本.然后,此过程将使用新复制的页面,而不是将来所有引用中的共享页面.另一个进程(未修改共享页面的进程)继续使用页面的原始副本(现在不再共享).这种技术称为写时复制,因为当某个进程写入页面时会复制该页面.
另外,要理解为什么这些程序似乎使用相同的内存空间(事实并非如此),我想引用"操作系统:原理与实践"一书的一部分.
大多数现代处理器引入了一种间接级别,称为虚拟地址.使用虚拟地址,每个进程的内存都从"相同"的位置开始,例如,零.每个过程都认为它拥有整个机器,尽管实际上并非如此.
所以这些虚拟地址是物理地址的翻译,并不代表相同的物理内存空间,如果我们编译并运行多次显示静态变量方向的程序,我们可以做一个更实际的测试示例,比如这个程序.
#include <stdio.h>
int main() {
static int a = 0;
printf("%p\n", &a);
getchar();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
如果我们直接处理物理内存,就不可能在两个不同的程序中获得相同的内存地址.
多次运行程序得到的结果是......

当内核fork()控制进程时,复制的内存信息继承相同的地址信息,因为堆是按原样有效复制的。如果地址不同,您将如何更新自定义结构内部的指针?内核对该信息一无所知,因此这些指针将失效。因此,物理地址可能会发生变化(事实上,即使在可执行文件的生命周期内,即使没有fork()ing,物理地址也经常会发生变化,但逻辑地址保持不变。
您可能正在使用虚拟内存的操作系统上运行程序.在fork()调用之后,父级和子级具有单独的地址空间,因此地址0x1370010不指向同一位置.如果一个进程写入*x,另一个进程将看不到更改.(实际上,这些内容可能是相同的内存页面,甚至是交换文件中的相同块,直到它被更改,但操作系统确保在父项或子项写入页面时立即复制页面,所以,只要该程序可以告诉它正在处理自己的副本.)