请考虑以下代码:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int g_a = 1;
int main(void){
int l_b = 1;
pid_t p;
if((p = vfork()) < 0){
perror("vfork");
return -1;
}else if(p == 0){
g_a++;
l_b++;
_exit(0); //use the system call _exit
}
printf("ppid = %d,pid = %d,g_a = %d,l_b = %d\n", getppid(), getpid(), g_a, l_b);
return 0;
}
结果如下: ppid = 21297,pid = 21553,g_a = 2,l_b = 2
但当我替换_exit(0);返回0;,结果变为:
ppid = 21297,pid = 21563,g_a = 2,l_b = -1216841009
分段错误
有一个分段问题,_exit和return之间有什么区别?
答案 0 :(得分:4)
从main
返回等同于调用exit
,它执行一系列清理操作(例如刷新打开的FILE
对象并运行atexit
过程)然后调用_exit
,这是实际终止进程的系统调用。因此,当您直接调用_exit
时,您将跳过所有这些清理操作。
您正在使用vfork
。除了调用vfork
或execve
之外,在_exit
的子方面执行任何是不正确的(正式地,它引起了未定义的行为)。从main
返回算作做其他事情(即调用exit
)。因此,程序崩溃并不奇怪。
编辑:就相关规范而言,修改g_a
子侧的变量l_b
和vfork
是也是禁止的,用同样可怕的术语(强烈意义上的“未定义行为”,即“允许导致程序崩溃”)。 但是,在我所知道的所有现存实现中,只有当子进行任何事情导致内存(包括堆栈帧)被分配或释放时,灾难才会发生。修改由父级分配但对子级可见的变量(无论是本地的还是全局的)变量是不可预测的:
vfork
只是fork
的另一个名称,那么孩子所做的一切都不会在父母身上看到; vfork
具有延迟分配新地址空间直到execve
的特殊行为,那么子对内存中驻留的变量的修改将在父节点恢复执行后的父节点。 (驻留在寄存器中的变量可能会或可能不会在内核恢复父执行上下文时重置,具体取决于上下文切换的完整程度。)而且,如果您知道您的操作系统是类型2,那么您可以使用此功能,例如在errno
失败后将execve
传递回父级,这对于普通fork
来说要困难得多(因为退出状态太窄)。然而,这是你逃避的事情,而不是你有权做的事情;特别是它是未来可携带性问题的根源。