Linux高级程序设计的第七章,看到fork和vfork的时候晕了,
先来看fork和vfork的区别,
1、fork调用成功后,子进程会复制父进程的几乎所有信息,当然pid除外,例如父进程的代码段,数据段,BSS,堆,栈,打开的文件描述符,另外,子进程还继承父进程的一些属性,比如实际用户/组号,有效用户/组号,保留的用户/组号,进程组号,环境变量,对文件的执行时的标识,信号的处理方式,信号掩码,当前工作目录,根目录,文件模式及创建掩码,文件大小限制等
这样的话,父子进程相互独立,互不影响。
2、而vfork具有更高的效率,因为vfork创建新进程时,无需完全复制父进程的地址空间,如果子进程只是执行类似exec()函数,就用不到父进程的数据空间
这样,父子进程会共享所有的资源,例如数据段和BSS段。
因此,使用vfork可能会出现问题,书上举了一个vfork导致父进程出现段错误例子。
#include <stdio.h> #include <unistd.h> #include <stdlib.h> void test() //test 函数, 在此函数中,调用vfork { pid_t pid; pid = vfork(); if(pid == -1) { perror("vfork"); exit(EXIT_FAILURE); } else if(pid == 0) //子进程中打印进程信息返回,从结果看,可以正常执行 { printf("1:child pid = %d, ppid = %d\n", getpid(), getppid()); return; } else //父进程打印进程信息,从结果看,可以正常执行 { printf("2:parent pid = %d, ppid = %d\n", getpid(), getppid()); } } void fun() //此函数代码,从结果看,在子进程中可以正常执行 { //但在父进程中没有能够执行,出现段错误 int i; int buf[100]; for(i = 0; i < 100; i++) { buf[i] = i; } printf("3:child pid = %d, ppid = %d\n", getpid(),getppid()); return; } int main(int argc, char** argv) { pid_t pid; //为了演示给出临时变量,没有使用 test(); //调用test printf("%d:after test\n", getpid()); fun(); //调用fun printf("%d:after fun\n", getpid()); return 0; }
执行结果:
1:child pid = 16358, ppid = 16357 16358:after test 3:child pid = 16358, ppid = 16357 16358:after fun 2:parent pid = 16357, ppid = 11820 Segmentation fault (core dumped)
这也说得通,但是,稍微如果我把fun函数改一下,
void fun() //此函数代码,从结果看,在子进程中可以正常执行 { //父进程中也没有能够执行,但是没有出现段错误 int i; int buf[100]; for(i = 0; i < 100; i++) { buf[i] = i; } printf("3:child pid = %d, ppid = %d\n", getpid(),getppid()); - return; + exit(0); }
1:child pid = 16398, ppid = 16397 16398:after test 3:child pid = 16398, ppid = 16397 2:parent pid = 16397, ppid = 11820 16397:after fun
先看return和exit的区别:
1、return只从函数主体中返回,exit退出当前进程,因此在主函数中使用return和exit是没有区别的
2、调用exit时要调用一段终止处理程序,然后关闭所有I/O流。注:_exit也不会处理标准I/O缓冲区。
好像帮助不大,我是这样理解的,首先不管调用fun的栈空间有没有被释放掉,当父进程返回的时候拿到的“返回地址”已经不是test函数的栈空间的“返回地址”了,前一个“返回地址"指向fun的下一条语句,也就是
printf("%d:after fun\n", getpid());而后一个“返回地址”指向的语句是,
printf("%d:after test\n", getpid());这样就解释了父进程为什么只打印了after fun,而没有打印after test
但是疑点仍然是为什么会出现段错误,我的理解是这样的,如果直接调用_exit,栈空间来不及释放,父进程退出test之时还能访问到覆盖后的栈空间,但是调用return,栈空间会立即释放,所以,父进程退出test函数,实际想执行after test这个地址的时候已经是非法了。