3
#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()。谁能帮我解决这个问题?

4

2 回答 2

2

父进程执行完后,会去wait(NULL);哪个阻塞父进程,直到子进程调用execl()or 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-25T10:10:36.477 回答
2

首先一条建议:不要使用vfork(). 在现代系统上,使用vfork()over的优势fork()是微乎其微的。您显示的代码永远无法正常使用,vfork()因为它会调用未定义的行为

POSIX.1:

vfork()函数与 具有相同的效果,除了如果由以下任一创建的进程修改了 用于存储返回值的类型变量以外的任何数据,或从调用的函数返回,或调用任何数据fork(),则行为未定义 成功调用之前的其他函数或函数族之一。vfork()pid_tvfork()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*()调用之后的任何东西无论如何都无法访问(这些函数永远不会返回,它们会替换正在运行的程序)!

由于其奇怪的语义和巨大的错误风险,在 POSIX.1-2008vfork()中被从 POSIX 中删除。一个安全且现代的 POSIX 替代 a 的典型用例vfork()直接跟随 a exec*()is posix_spawn()


总而言之,你真的不应该使用vfork(). 改为使用fork()。删除无法访问的调用exit(),您的程序看起来很好。

于 2017-07-25T11:14:26.917 回答