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