【问题标题】:Why does an exiting child process cause the parent's sigsuspend call to return on macOS?为什么退出的子进程会导致父进程的 sigsuspend 调用在 macOS 上返回?
【发布时间】:2020-09-26 20:59:23
【问题描述】:

考虑以下用于 POSIX 系统的 C 代码:

#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>

#define CONTINUE_SIGNAL SIGINT

void continue_handler(int sig, siginfo_t *info, void *context)
{
    printf("[P] Continuing process with pid = %d...\n", getpid());
}

int main(void)
{
    struct sigaction act;
    sigset_t mask;
    pid_t pid;

    // Block the CONTINUE_SIGNAL for now.
    sigemptyset(&mask);
    sigaddset(&mask, CONTINUE_SIGNAL);
    sigprocmask(SIG_BLOCK, &mask, NULL);

    if ((pid = fork()) == 0)  {
        printf("[C] This is the child (pid %d).\n", getpid());
        return 0;
    }

    printf("[P] Parent (pid %d) has spawned child (pid %d).\n", getpid(), pid);

    // Call the 'continue_handler' when CONTINUE_SIGNAL is received.
    act.sa_sigaction = continue_handler;
    act.sa_flags = SA_SIGINFO;
    sigaction(CONTINUE_SIGNAL, &act, NULL);
    printf("[P] Waiting for CONTINUE_SIGNAL...\n");

    // Block all signals except CONTINUE_SIGNAL and wait for it to occur.
    sigfillset(&mask);
    sigdelset(&mask, CONTINUE_SIGNAL);
    sigsuspend(&mask);
    printf("[P] Signal received. Exiting...\n");

    return 0;
}

请注意,我已删除所有错误检查,以便能够以更紧凑的形式表示它。但是,我已经验证了以下两种情况下所有函数都成功返回。

在 Linux 系统上,代码完全符合我的预期:父进程生成一个子进程并等待SIGINT,然后继续执行continue_handler

[P] Parent (pid 804) has spawned child (pid 805).
[P] Waiting for CONTINUE_SIGNAL...
[C] This is the child (pid 805).
  <press Ctrl+C>
[P] Continuing process with pid = 804...
[P] Signal received. Exiting...

然而,在 macOS 上,我获得了以下输出(没有任何交互):

[P] Parent (pid 6024) has spawned child (pid 6025).
[P] Waiting for CONTINUE_SIGNAL...
[C] This is the child (pid 6025).
[P] Signal received. Exiting...

显然,父进程(pid 6024)的sigsuspend调用在子进程退出后立即返回。尽管SIGINT 似乎没有被触发,但sigsuspend 会返回EINTRerrno,即报告一个非屏蔽信号成功终止。请注意,如果我阻止子进程退出,sigsuspend自行返回,而是等待SIGINT 被传递。

我使用 API 的方式有问题吗?或者,POSIX 规范中是否存在一定程度的灵活性,可以使这两种行为都符合预期?

【问题讨论】:

  • 您不能从信号处理程序中安全地调用printf()。在严格符合 C 语言中,调用任何库函数都是不安全的。每footnote 188 of the C 11 standard:“因此,信号处理程序通常不能调用标准库函数。” POSIX allows for the calling of async-signal-safe functions 并且只有信号处理程序中的异步信号安全功能。 printf() 不是异步信号安全的。
  • @AndrewHenle 这不是这个程序关心的问题,因为只有当主要控制流在 sigsuspend 中被阻塞时才会传递信号。
  • @bosonic 恐怕我很难过。它几乎闻起来像 OSX 的 sigsuspend 实现中的一个错误。你能在一个完全开源的 BSD 上测试你的程序吗?如果您可以在那里重现相同的行为,那就更容易理解了。
  • 这可以在没有 sigaction 并发症的情况下进行演示。设置一个五秒钟的闹钟。在终止之前分叉一个睡了一两秒钟的孩子。让父 sigsuspend 只允许 SIGALRM。在 OS X 上,父级的 sigsuspend 将被 EINTRrupted 并继续执行;而在 Linux 上,父节点被闹钟终止。
  • @user3629249,信号处理程序和正常的收获做法是红鲱鱼。这里的问题是,为什么在 OS X 上,sigsuspend with SIGCHLD masked off被子进程终止中断了?

标签: c unix fork posix


【解决方案1】:

请阅读:signals in MAX OS X

尤其是声明:

To ignore the signal, func should be SIG_IGN. 

不知何故,我在您的代码中没有看到。

【讨论】:

    猜你喜欢
    • 2021-04-26
    • 2011-01-21
    • 2011-02-12
    • 1970-01-01
    • 2011-08-04
    • 2019-01-05
    • 2020-12-12
    • 2012-06-11
    • 1970-01-01
    相关资源
    最近更新 更多