#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我的问题是:为什么在执行父进程之后打印"hello“?我已经开始学习vfork()了。有人能帮我吗?
发布于 2017-07-25 10:10:36
执行父进程后,它将进入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)手册页。)
发布于 2017-07-25 11:14:26
首先有一条建议:不要使用vfork()。在现代系统中,使用vfork()而不是fork()的优势很小。您显示的代码无法在vfork()中正常工作,因为它调用了未定义的行为。
POSIX.1:
vfork()函数具有与fork()相同的效果,但如果vfork()创建的进程修改用于存储来自vfork()的返回值的类型pid_t变量以外的任何数据,或者从调用vfork()的函数中返回,或者在成功调用_exit()或exec()系列函数之前调用任何其他函数,则行为是未定义的。
因此,通过在子代码中调用printf(),您的代码已经没有定义。请注意,即使调用exit()也会导致未定义的行为,只允许使用_exit()。
我假设您在Linux上尝试了这一点,它更多地定义了vfork()的行为,并解释了您所观察到的内容:
来自linux manual page的
vfork()与fork(2)的不同之处在于,调用线程被挂起,直到子线程终止为止(通常通过调用_exit(2),或者在传递致命信号之后调用_exit(2)),或者调用execve(2)。在此之前,子节点与其父节点共享所有内存,包括堆栈。
因此,在Linux上,您可以确保首先执行由vfork()创建的子进程,并且只通过调用execl() (在内部调用execve() )来执行,允许父进程继续运行。这就是为什么您在子输出之后看到父输出的原因。一旦父调用了wait(),它就一直等到子进程完成--此时,子节点被echo替换。
依赖这种行为使您的程序无法移植到不同的vfork()实现。对printf()的调用是可以的,因为Linux挂起了父进程,而对exit()的错误调用在这里并不重要:无论如何,在exec*()调用之后的任何事情都是不可访问的(这些函数永远不会返回,它们将替换为正在运行的程序)!
由于其怪异的语义和巨大的bug风险,vfork()在POSIX1.2008中被从POSIX中删除。一个安全和现代的POSIX替代vfork()的典型用法是exec*(),后面跟着的是posix_spawn()。
总之,您真的不应该使用vfork()。使用fork()代替。删除对exit()的不可访问调用,您的程序看起来也很好。
https://stackoverflow.com/questions/45299735
复制相似问题