我有一个关于发送信号的c的问题 这是我的密码 #包括 #包括 #包括 #包括 #包括 #包括 #包括 无效手柄1(内部信号){ int-pid; 如果((pid=waitpid(-1,NULL,0))

我有一个关于发送信号的c的问题 这是我的密码 #包括 #包括 #包括 #包括 #包括 #包括 #包括 无效手柄1(内部信号){ int-pid; 如果((pid=waitpid(-1,NULL,0)),c,C,当我运行它时,它显示如下: 父亲爱你! 你好,孩子7843 你好,孩子7844 你好,孩子7842 汉德勒收获了孩子7842 但我认为应该是这样 父亲爱你! 你好,孩子7843 你好,孩子7844 你好,孩子7842 汉德勒收获了孩子7842 汉德勒收获了孩子7843 有一个重复的处理程序收获了子xx 如果我取消注释sleep(1),它将显示我想要的内容: Hello来自child 7858 汉德勒收获了孩子7858 你好,孩子7859 儿童7860你好 汉德勒收获了孩子7859 父亲爱你!

当我运行它时,它显示如下:

父亲爱你!
你好,孩子7843
你好,孩子7844
你好,孩子7842
汉德勒收获了孩子7842
但我认为应该是这样

父亲爱你!
你好,孩子7843
你好,孩子7844
你好,孩子7842
汉德勒收获了孩子7842
汉德勒收获了孩子7843
有一个重复的
处理程序收获了子xx

如果我取消注释
sleep(1),它将显示我想要的内容:

Hello来自child 7858
汉德勒收获了孩子7858
你好,孩子7859
儿童7860你好
汉德勒收获了孩子7859
父亲爱你!
我不知道为什么第一个只有一个孩子。
请帮助我,谢谢您的建议。

操作系统只允许发送一个
SIGCHLD
信号来告诉您几个子进程已退出。这意味着您需要在信号处理程序中循环调用
waitpid
,如下所示:

void sigchld_handler(int unused)
{
    pid_t pid;
    int status;
    for (;;) {
        pid = waitpid(-1, &status, WNOHANG);
        if (pid == -1) {
            if (errno != ECHILD)
                printf("waitpid failure: %s\n", strerror(errno);
            return;
        }
        printf("child %d exit status %d\n", (int)pid, status);
    }
}
您需要使用
WNOHANG
以便
waitpid
将失败,并在没有更多进程等待时将
errno
设置为
ECHILD
,而不是阻塞(可能永远)

此外,为了安全地从信号处理程序内部调用
printf
,您需要在主函数中使用
sigprocmask
sigsupend
,以便只有在
sigsupend
上阻止正常执行时才能发送信号。此外,切勿使用
信号
,仅使用
sigaction
signal
的规范没有涵盖几个重要的细节,这些细节会让你感到难受

int main(void)
{
   sigset_t sigchld_set;
   sigemptyset(&sigchld_set);
   sigaddset(&sigchild_set, SIGCHLD);

   sigset_t unblock_sigchld_set;
   if (sigprocmask(SIG_BLOCK, &sigchld_set, &unblock_sigchld_set)) {
       perror("sigprocmask");
       return 1;
   }
   sigdelset(SIGCHLD, &unblock_sigchld_set);

   struct sigaction sa;
   sigfillset(&sa.sa_mask);
   sa.sa_handler = sigchld_handler;
   sa.sa_flags = SA_RESTART;
   if (sigaction(SIGCHLD, &sa, 0)) {
       perror("sigaction");
       return 1;
   }

   /* fork loop goes here */

   for (;;)
       sigsuspend(&unblock_sigchld_set);

   return 0;
}

当所有子进程都已等待时,使
main
退出作为练习。(提示:您需要C11。)

您无法从信号处理程序中安全地调用函数,如
printf()
sleep()
。这样做会导致未定义的行为。您只能在信号处理程序中安全地调用异步信号安全函数。@AndrewHenle
sleep
是异步信号安全的(无论如何,在POSIX.1-2008中,我知道它是用
alarm
pause
在过去的日子里实现的,不会)。关于printf的
printf
,你是对的,尽管“你不能”太强了(见我的答案)。@zwol有趣的是,这一点。“在单线程程序中,sleep()可能会使用SIGALRM。”我不知道如何实现异步信号安全。也被列为
MT-Unsafe-sig:SIGCHLD/linux
,尽管它似乎与这个问题相关。(续)和@zwol-sleep在POSIX本身中是异步信号安全的,我的两点是linux忽略了同样的要求,因为它适用于
fork()
因此,调用POSIX来确定一个可能的Linux系统应该做什么并没有很好的记录,
sleep()
被列为
MT unsafe
,关于
SIGCHLD
,这似乎非常适用于这个问题。非常感谢您。我需要一些时间来消化你教的东西。我已将
printf
更改为
write
并删除
sleep
。但我仍然只有一个信号。在CSAPP的书中,它告诉我:我将收到两个信号(第三个信号已被丢弃),一个来自运行,另一个来自挂起。你能告诉我为什么我只收到一个信号,而不是两个吗?你唯一能确定的是你至少会收到一个信号。如果你的书上说你会得到一些具体的数字,那就错了。