在coolshell.cn上看到的一个问题,为此拿来研究一下。
首先 看看return和exit的区别
在linux上分别跑一下这个代码
int main()
{
return 0;
//exit(0);
}
return 0
exit(0)
return在返回时会对栈进行操作,将栈清空,然后跳转到调用函数的下一条指令,而exit没有对栈操作,具体exit怎么运行,会在linux0.12内核里面研究,
看看下面一段代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void) {
int var;
int pid;
var = 88;
if ((pid = vfork()) < 0) {
printf("vfork error");
exit(-1);
} else if (pid == 0) { /* 子进程 */
var++;
return 0;
}
printf("pid=%d, var=%d\n", getpid(), var);
return 0;
}
运行之后
很难想象
如果将return 换成 exit
就运行正常。神奇的很,为此分析一下。
在看看使用的这个函数vfork
如果改成fork会是什么样
如下
fork+exit
运行正常
fork+return
也是可以正常运行的,
所以还要研究一下fork和vfork
man fork
Historic description
Under Linux, fork(2) is implemented using copy-on-write pages, so the
only penalty incurred by fork(2) is the time and memory required to
duplicate the parent’s page tables, and to create a unique task
structure for the child. However, in the bad old days a fork(2)
would require making a complete copy of the caller’s data space,
often needlessly, since usually immediately afterward an exec(3) is
done. Thus, for greater efficiency, BSD introduced the vfork()
system call, which did not fully copy the address space of the parent
process, but borrowed the parent’s memory and thread of control until
a call to execve(2) or an exit occurred. The parent process was
suspended while the child was using its resources. The use of
vfork() was tricky: for example, not modifying data in the parent
process depended on knowing which variables were held in a register.
有两点比较重要
1、
the vfork()
system call, which did not fully copy the address space of the parent
process, but borrowed the parent’s memory and thread of control until
a call to execve(2) or an exit occurred.
2、
The parent process was
suspended while the child was using its resources.
也就是vfork是和父进程共享内存的,这里包括栈空间。当调用execve或者exit才会停止这种共享,而且父进程在子进程运行时是挂起的,
试验一下
测试fork是不是copy_on_write,并且两个进程是同时运行的
修改为
运行结果为
可以看出,var两个进程不共享,且两个进程执行并无先后顺序
同样上面那段代码,再修改为 vfork
运行
始终是son先运行,然后exit之后父进程再运行,并且父进程运行时,依旧共享var的值
所以到此这个问题也就可以就是了
vfork是子进程共享父进程栈,而return指令是清空栈,返回调用main的函数,而exit不会清理栈的内容。
子进程通过return返回后,父进程会去执行,而此时栈里面已经清空,所以会出问题。
为什么会出项执行多次父进程,网上有人讲,是有的os会重新调用main。