【问题标题】:fork + wait gets EINTR on OSXfork + wait 在 OSX 上获取 EINTR
【发布时间】:2013-12-31 13:48:33
【问题描述】:

我在 fork 101 失败。我希望这会 fork 一个子进程,并输出子进程和父进程 printfs:

pid_t fpid;

if ((fpid = fork()) < 0)
{
    printf("fork: %s\n", strerror(errno));
    exit(-1);
}

if (0 == fpid) // child
{
    printf("\nI am the child\n");
}
else
{
    printf("\nI am the parent\n");
    pid_t wpid;
    while ((wpid = waitpid(WAIT_ANY, NULL, 0)))
    {
        if (errno == ECHILD)
            break;
        else if (wpid < 0)
            printf("wait: %s\n", strerror(errno));
    }
}

相反,我得到了这个输出:

我是家长

等待:系统调用中断

所以我的问题是:为什么孩子没有机会生活和跑步?会不会有人请想想孩子们!另外,EINTR 是从哪里来的?显然,这与我的第一个问题有关。

此外,当我在独立程序中运行该代码时,它可以正常运行,但在我的更大程序中运行时却不行;更大的程序可以做什么来扰乱waitpid?

FWIW 这是在 OSX 10.9 上。

【问题讨论】:

  • 孩子不发送SIGCHLD吗?也许屏蔽那个信号?
  • 很奇怪,它可以在我的 Linux 机器上运行。我对 OSX 不太了解...它是否具有某种沙盒功能或需要特殊的应用程序权限才能执行 fork()
  • 在 OSX 的近亲 FreeBSD 上运行良好。
  • 我没有在任何地方明确使用 SIGCHLD,但我正在使用 setjmp(),这可能是相关的吗?
  • wpidwaitpid() 之后的值是多少?如果它不是-1,那么您无权查看errno。有关相关场景,请参阅 errno set in child process after fork() in Mac OS X

标签: c macos unix fork


【解决方案1】:

在 OSX 上,在执行之前/不执行时在 fork 的子端做太多事情是不合法的。请参阅fork man page 底部的警告。安全功能列表位于sigaction(2) man pageprintf() 不在其中。

此外,stdout 可能是缓冲的。 printf() 的结果可能不会被刷新。如果您调用exit(),它将被刷新,但这在叉子的子端也是不合法的。 (使用_exit() 是合适的,但这不会刷新打开的流。)事实上,您似乎没有退出您的子进程,这意味着执行流程继续到您显示的代码的调用者,大概会返回到程序的其余部分。由于 fork 子端的限制,它可能会卡在那里。

如果你在孩子身上做这样的事情,你可能会有更多的运气:

const char msg[] = "\nI am the child\n";
write(STDOUT_FILENO, msg, sizeof(msg) - 1);
_exit(0);

最后,我认为你应该将fpid 而不是WAIT_ANY 传递给waitpid()。您有一个要等待的特定子进程。在大型程序的上下文中,您不想窃取由其他子组件生成的子组件终止的通知。而且你总是需要循环可中断的系统调用,直到它们返回 EINTR 以外的东西。

【讨论】:

  • 所以警告说“在这种情况下,执行你自己是合理的”,这是我需要做的。如果应用程序不一定在工作目录中,我如何知道传递给 exec 以指示“我自己”的内容?
  • 鉴于您使用的是 OS X,您可以使用_NSGetExecutablePath()。如果你使用的是 Cocoa,你可以使用[[[NSBundle mainBundle] executablePath] fileSystemRepresentation]。但是,您必须在分叉之前调用它们。当然,当你自己执行时,你需要传递一个特殊的参数来让新进程知道不能正常运行应用程序。如果应用程序捆绑和/或链接到 AppKit,子进程可能会获得一个单独的 Dock 图标,这是您不想要的。
  • @Gilles,限制是 POSIX 规范的一部分。这也是常识。当你 fork 时,只有调用 fork() 的线程才能存活到子进程中。其他人干脆消失了。那些其他线程可能一直持有共享资源,例如系统库中的锁。因此,调用任何非异步取消安全例程都容易锁定或发现内部数据结构处于不一致状态。
  • @KenThomases 在大多数 unice 上,这只适用于多线程程序。否则,在传统的 unix 系统上,fork 会复制该进程,接下来发生什么并不重要。还有其他可能的 nast 影响,例如 stdio 缓冲区被复制,但没有要求在单线程程序中避免 fork 之后的某些库函数。
  • @Gilles,但不能仅仅因为应用程序代码没有创建任何其他线程就保证程序是单线程的。系统库可以将线程用于自己的目的。实际上,您必须假设所有程序都是多线程的。
【解决方案2】:

仅在指示失败时检查errno,例如通过返回-1 的系统调用。

代码应如下所示:

pid_t fpid;

if ((fpid = fork()) < 0)
{
    printf("fork: %s\n", strerror(errno));
    exit(-1);
}

if (0 == fpid) // child
{
    printf("\nI am the child\n");
}
else
{
    printf("\nI am the parent\n");

    pid_t wpid;
    while ((wpid = waitpid(WAIT_ANY, NULL, 0)))
    {
        if (-1 == wpid)
        {
            perror("waitpid() failed");
        }
        else if (wpid == fpid)
        {
            /* My child ended, so stop waiting for it. */
            break;
        }
    }
}

【讨论】:

    猜你喜欢
    • 2014-04-21
    • 2015-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 2013-10-06
    • 2011-02-12
    相关资源
    最近更新 更多