1、共享正文段
下面是用fork创建一个子进程的例子(参考资料[1]程序清单8-1):
#include <stdio.h>
#include <unistd.h>
int glob = 6; // external variable in initialized data
char buf[] = "a write to stdout\n";
int main(void){
int var; // automatic variable on the stack
pid_t pid;
var = 88;
if(write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1)
printf("write error\n");
printf("before fork\n"); //we don't flush stdout
if((pid = fork()) < 0)
printf("write error");
else if(pid == 0) { // child
glob++;
var++;
}
else { // parent
sleep(2);
}
printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var);
return(0);
}
根据父/子进程的注释,很自然的以为else if(pid==0){}中的代码属于子进程,而else{}中的代码属于父进程。这样就有个疑惑:除了else if(pid==0){}和else{}之外的printf("pid=%d, ...")是属于谁的呢?
其实,上面的理解是错的。根据参考资料[1]P172的描述:“子进程和父进程继续执行fork调用之后的指令”。也就是说,不管是else if(pid==0){}还是else{},都是父子进程所共有的,只是在子进程中fork的返回值pid=0,因此会执行else if(pid==0){}一段,而父进程中fork的返回值pid>0,因此执行else{}一段。至于printf(“pid=%d, ...”),父子进程都会执行!
参考资料[1]P153提到,正文段(CPU执行的指令部分)是可以共享的。参考资料[1]P172提到:父、子进程共享正文段。
下面是上面例子的运行结果:
a write to stdout
before fork
pid = 3204, glob = 7, var = 89
pid = 3203, glob = 6, var = 88
由此可见,printf("pid=%d,....")被执行了2次,一次是在父进程中执行,pid=3203,;另外一次是在子进程中执行,pid=3204。从而印证了上面的说法。
然而,fork()以上的write()只被执行1次,说明父子进程共享的正文段是从fork()之后开始的。
2、数据段、堆栈
参考资料[1]提到,子进程复制父进程的数据段、堆、栈的副本,但是不共享。也就是说子进程只是将父进程的数据段、堆、栈父子了一份到自己的存储空间了,并非和父进程共享这些空间。由上面的执行结果可以知道,父子进程分别有自己的glob、var变量的存储空间,而且子进程的glob、var变量的初始值是从父进程中复制进来的。
参考资料
[1]《UNIX环境高级编程》.2nd