#include <stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
pid_t child_pid = vfork();
if(child_pid < 0)
{
printf("vfork() error\n");
exit(-1);
}
if(child_pid != 0)
{
printf("Hey I am parent %d\nMy child is %d\n",getpid(),child_pid);
wait(NULL);
}
else
{
printf("Hey I am child %d\nMy parent is %d\n",getpid(),getppid());
execl("/bin/echo","echo","hello",NULL);
exit(0);
}
return 0;
}
输出:
Hey I am child 4
My parent is 3
Hey I am parent 3
My child is 4
hello
我的问题:为什么&#34;你好&#34;父进程执行后打印? 我已经开始学习vfork()了。有人可以帮帮我吗?
答案 0 :(得分:2)
执行父进程后,它将转到wait(NULL);
,阻止父进程,直到子进程调用execl()
或exit
。
因此,当父项被阻止时子进程正在调用execl()
,因此hello
正在输出的末尾打印。
#include <stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
pid_t child_pid = vfork();
if(child_pid < 0)
{
printf("vfork() error\n");
exit(-1);
}
if(child_pid != 0)
{
printf("Hey I am parent %d\nMy child is %d\n",getpid(),child_pid);
wait(NULL);
}
else
{
printf("Hey I am child %d\nMy parent is %d\n",getpid(),getppid());
execl("/bin/echo","echo","hello",NULL);
exit(0);
}
return 0;
}
如果您移除execl()
,则子进程将转到exit(0)
并且hello
将不会被打印。
此外,在执行execl()
之后,它正在创建一个新进程,因此在execl()
之后编写的任何代码都不会被执行。
根据手册页: -
exec()系列函数用新的过程映像替换当前过程映像。本手册页中描述的功能是execve(2)的前端。 (有关更换当前过程映像的更多详细信息,请参见execve(2)的手册页。)
答案 1 :(得分:2)
首先建议:不要使用vfork()
。在现代系统中,使用vfork()
而非fork()
的优势很小。您显示的代码永远无法与vfork()
一起正常运行,因为它会调用未定义的行为:
<强> POSIX.1:强>
vfork()
功能与fork()
具有相同的效果, 除了如果创建的进程由行为未定义vfork()
修改pid_t
类型变量以外的任何数据 用于存储来自vfork()
的返回值,或从中返回 调用vfork()
的函数,或调用任何其他函数 在成功调用_exit()
或其中一个exec()
系列之前 功能
因此,通过在子代码中调用printf()
,您的代码已经未定义。请注意,即使调用exit()
也会导致未定义的行为,只允许_exit()
。
我假设您在Linux上尝试此操作,它更多地定义了vfork()
的行为并以某种方式解释了您所观察到的内容:
来自linux vfork()
manual page:
vfork()
与fork(2)
的不同之处在于调用线程已暂停 直到孩子终止(通常是通过呼叫_exit(2)
,或者 异常,在发出致命信号后),或拨打电话execve(2)
。在那之前,孩子与其共享所有记忆 父母,包括筹码。
所以在Linux上,你可以确定vfork()
创建的子项是先执行的,只能通过调用execl()
(内部调用execve()
),父进程是允许继续运行。这就是您在孩子输出后看到父母输出的原因。一旦家长呼叫wait()
,它就会一直等到孩子完成 - 此时,孩子会被echo
取代。
依赖此行为会使您的程序无法移植到vfork()
的不同实现。对printf()
的调用很好,因为Linux暂停了父进程,错误对exit()
的调用并不重要,因为运气:{{1}之后的任何内容无论如何调用是无法访问的(这些函数永远不会返回,它们替换正在运行的程序)!
由于其奇怪的语义和巨大的错误风险,exec*()
在POSIX.1-2008中从POSIX中删除了 。对于vfork()
的典型用例,直接后跟vfork()
的安全且现代的POSIX替换为posix_spawn()
。
总而言之,你真的不应该使用exec*()
。请改用vfork()
。删除对fork()
的无法访问的呼叫,您的程序看起来很好。