昨天在测试一道题时,突然发现的,然后在网上找,发现这个问题还是挺重要的,为以后学习打下基础.
int main()
{
pid_t pid;
pid=fork(); //1
pid=vfork(); //2
if(pid<0)
{
printf("error");
}
if(pid>0)
{
....
exit(0);
}
if(pid==0)
{
....
exit(0); //3
return 0; //4
}
}
上述代码很简单就只是用fork/vfork来创建了个进程,但结束子进程方式有两种return /exit();
我们知道让进程正常退出的方法有return/exit()/_exit();可是在这用fork创建子进程用上述方法退不会有问题,问题就出在,如果用vfork创建时,只能用exit退出,如果你用return推迟时,会出现段错误,或者其他严重的后果;why?
问题就出在vfork上,用vfork创建的进程与fork创建的进程主要区别有两点:
1>fork创建进程后,父子进程是分开的,子几乎完全复制父的资源,二者互不干扰,具有良好的并发性;而vfork则是为了减少资源复制,所以就造成了,子父进程完全是共享的,就是说,他们对数据的修改,两者完全可见,共用一段地址;
2>fork创建的父子进程二者执行的顺序是不确定的,这取决于,内核的调用算法;而vfork不一样,他保证了子进程的先执行,直到子进程调用exit/_exit退出才会执行父进程,在这期间,父进程一直等待;
上述问题的原因就在与,当用vfork时子进程先执行,然后就return让子进程的main函数返回了,main函数return后,会退栈,释放局部变量(而exit则不会修改栈,直接返回),会调用exit类的函数,此时,父进程收到子进程exit才开始执行,但是父进程的栈被子进程给退了(二者是共享的),所以这就让父进程不知怎么办,就会报栈错误;
补充:
1>因为vfork()是共享地址空间的,所以父子进程才不会同时执行,否则会破坏栈,导致不可预知的错误,
2>return会退栈而,exit和execve族两个系统调用是不退栈的,而是直接进入系统空间,讲共享地址分开.
再看一个在网上找的vfork子进程改变栈的例子
#include<stdio.h>
#include<unistd.h>
void stack1()
{
vfork();
}
void stack2()
{
_exit(0);
}
int main()
{
stack1();
printf("%d 1\n",getpid());
stack2();
printf("%d 2\n",getpid());
return 0;
}
猜猜这段代码会执行出什么结果
我的执行结果是
8546 1 //子进程
8545 2 //父进程
为什么?为什么父子进程都出来了?
因为当程序执行到stack1时,调用vfork父进程会阻塞在这里,此时子进程先从vfork调用返回,接着再从stack1返回,会让stack1退栈,接着灰进入到stack2,碰见_exit();子进程结束,但它会将栈修改为_exit()的后一句指令,由于父子是共享栈的,所以会导致将父进程的栈点已经从vfork()的后一句指令修改为_exit()的后一句指令了,所以父进程实际上是从stack2()返回的.