C语言 关于 vfork() 系统调用?


#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()的无法访问的调用,您的程序看起来很好。

相关内容

  • 没有找到相关文章