vfork永远不会结束

时间:2013-01-23 12:43:46

标签: c linux fork vfork

以下代码永远不会结束。这是为什么?

#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#define SIZE 5
int nums[SIZE] = {0, 1, 2, 3, 4};
int main()
{
  int i;
  pid_t pid;
  pid = vfork();
  if(pid == 0){  /* Child process */
    for(i = 0; i < SIZE; i++){
      nums[i] *= -i;
      printf(”CHILD: %d “, nums[i]);    /* LINE X */
    }
  }
  else if (pid > 0){  /* Parent process */
    wait(NULL);
    for(i = 0; i < SIZE; i++)
      printf(”PARENT: %d “, nums[i]);   /* LINE Y */
  }
  return 0;
}

更新

这段代码只是为了说明我对vfork()的一些困惑。看起来当我使用vfork()时,子进程不会复制父进程的地址空间。相反,它共享地址空间。在这种情况下,我希望nums数组可以通过这两个进程更新,我的问题是以什么顺序?操作系统如何在两者之间同步?

至于为什么代码永远不会结束,可能是因为我没有明确地为退出提供任何_exit()exec()语句。我是对的吗?

UPDATE2:
我刚读过:56. Difference between the fork() and vfork() system call? 我觉得这篇文章让我第一次感到困惑。

  

来自vfork()系统调用的子进程在父进程中执行   地址空间(这可以覆盖父数据和堆栈)   暂停父进程,直到子进程退出。

5 个答案:

答案 0 :(得分:8)

引用vfork(2)手册页:

  

vfork()函数与fork()具有相同的效果,除非行为未定义如果vfork()创建的进程修改了除pid_t类型的变量之外的任何数据存储来自vfork()的返回值,或者从调用vfork()的函数返回,或者在成功调用_exit()或exec系列函数之前调用任何其他函数。

你正在做很多这样的事情,所以你不应该期望它能够发挥作用。我认为这里真正的问题是:你为什么使用vfork()而不是fork()

答案 1 :(得分:3)

请勿使用vfork。这是你能得到的最简单的建议。 vfork向您提供的唯一内容是暂停父级,直到孩子调用exec*_exit。关于共享地址空间的部分是不正确的,有些操作系统是这样做的,其他操作系统选择不这样做,因为它非常不安全并且导致了严重的错误。

上次我查看应用程序如何使用vfork实际上绝对多数做错了。这是非常糟糕的,我扔掉了6个字符的更改,在我当时正在处理的操作系统上启用了地址空间共享。几乎所有使用vfork的人如果不是更糟,至少会泄漏记忆。

如果您真的想使用vfork,除了在子进程中返回后立即调用_exitexecve之外,请不要执行任何操作。还有其他任何东西,你进入了未定义的领域。而我的意思是“任何事情”。你开始解析你的字符串来为你的exec调用创建参数,并且你几乎可以肯定某些东西会触及它不应该触及的东西。我的意思是execve,而不是exec家族的其他功能。许多libc在execvpexeclexecle等处执行vfork上下文中不安全的事情。

您的示例中特别发生了什么:

  

如果你的操作系统共享地址空间,那么从main返回的子代表意味着你的环境清理了东西(自你调用printf后刷新stdout,由printf分配的空闲内存等等)。这意味着还有其他函数会覆盖父级卡在的堆栈帧。在父级中返回的vfork返回到已被覆盖的堆栈帧,任何事情都可能发生,甚至可能没有返回堆栈上的地址将再次返回。您首先通过调用printf进入未定义的行为国家,然后从main返回将您带入未定义的行为大陆,并且从main返回后清理运行使您前往未定义的行为星球。

答案 2 :(得分:1)

来自official specification

  

如果vfork()创建的进程修改了除用于存储vfork()的返回值的pid_t类型的变量以外的任何数据,则行为未定义,

在您的程序中,您修改pid变量以外的数据,这意味着行为未定义。

您还必须致电_exit以结束此流程,或致电exec系列功能。

答案 3 :(得分:0)

孩子必须_exit而不是从main返回。如果子进程从main返回,则父进程从vfork返回时,堆栈框架不存在。

答案 4 :(得分:0)

只需调用_exit而不是调用return或将_exit(0)插入“子进程”的最后一行。在关闭stdout时返回0调用exit(0),所以当另一个printf跟随时,程序崩溃。