关于vfork()系统调用?

时间:2017-07-25 09:51:48

标签: c posix 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() 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()了。有人可以帮帮我吗?

2 个答案:

答案 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()的无法访问的呼叫,您的程序看起来很好。