2

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

#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. fork() 和 vfork() 系统调用之间的区别? 我认为这篇文章可以帮助我解决我的第一个困惑。

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

4

5 回答 5

8

引用vfork(2)手册页:

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

你正在做一大堆这样的事情,所以你不应该期望它会起作用。我认为这里真正的问题是:为什么你使用vfork()而不是fork()

于 2013-01-23T12:50:18.943 回答
3

不要使用vfork. 这是你能得到的最简单的建议。唯一vfork能给你的是暂停父母,直到孩子打电话exec*_exit。关于共享地址空间的部分是不正确的,一些操作系统这样做,其他选择不这样做,因为它非常不安全并且导致了严重的错误。

上次我研究了应用程序vfork在现实中的使用方式,绝大多数人都做错了。太糟糕了,我放弃了在当时正在开发的操作系统上启用地址空间共享的 6 个字符更改。几乎每个使用vfork至少的人都会泄漏内存,如果不是更糟的话。

如果您真的想使用,除了立即调用或在子进程中返回后vfork不要做任何事情。其他任何事情,您都将进入未定义的领域。我的意思是“任何东西”。你开始解析你的字符串来为你的 exec 调用做参数,你几乎可以保证某些东西会触及它不应该触及的东西。而且我的意思是,不是 exec 家族的其他一些功能。许多 libc 在, ,等中做一些在上下文中不安全的事情。_exitexecveexecveexecvpexeclexeclevfork

您的示例中具体发生了什么:

如果您的操作系统共享地址空间,则从 main 返回的孩子意味着您的环境已清理干净(因为您调用了 printf,所以刷新标准输出、由 printf 分配的空闲内存等)。这意味着还有其他调用的函数将覆盖父级卡在的堆栈帧。vfork返回父级返回已被覆盖的堆栈帧并且任何事情都可能发生,它甚至可能在堆栈上没有返回地址返回了。你先调用printf进入未定义行为国家,然后main返回带你进入未定义行为大陆,main返回后的清理运行使你旅行到未定义行为星球。

于 2013-01-23T14:26:24.363 回答
1

来自官方规范

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

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

您还必须调用_exit以结束进程,或调用exec函数族之一。

于 2013-01-23T12:50:06.667 回答
0

孩子必须_exit而不是从main. 如果子进程从 中返回main,则父进程从 中返回时的堆栈帧不存在vfork

于 2013-01-23T12:49:25.393 回答
0

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

于 2013-01-23T13:13:16.873 回答