您的代码经过某种程度的清理后成为 MCVE (Minimal, Complete, Verifiable Example):
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
static volatile sig_atomic_t count = 0;
static void handler(int sig)
{
assert(sig == SIGCHLD);
count++;
}
int main(void)
{
signal(SIGCHLD, handler);
for (int i = 0; i < 4; i++)
{
if (fork() == 0)
{
exit(16 * (i + 1));
}
}
int corpse;
int status;
while ((corpse = wait(&status)) != -1 || errno == EINTR)
{
if (corpse == -1 && errno == EINTR)
printf("Interrupted by a signal\n");
else
printf("Child %d exited with status 0x%.4X\n", corpse, status);
}
printf("Count = %d\n", count);
return 0;
}
在运行 macOS Sierra 10.12.4 和 GCC 6.3.0 的 Mac 上运行时,我得到了以下主题的变体:
Child 74003 exited with status 0x1000
Child 74004 exited with status 0x2000
Child 74005 exited with status 0x3000
Child 74006 exited with status 0x4000
Count = 4
在这台机器(一台现代 15 英寸 2016 MacBook Pro)上,我似乎总是将其作为输出 - 依次为子进程 ID 和精心定制的退出状态。
当我像这样更改处理程序时(记住how to avoid calling printf() in a signal handler 的限制——是的,我知道我可以输入STDIN_FILENO 而不是其中的一些1s):
static void handler(int sig)
{
assert(sig == SIGCHLD);
count++;
char s[2] = { count + '0', '\n' };
write(1, "SH: count = ", sizeof("SH: count = ")-1);
write(1, s, 2);
}
然后输出变为更像这样的东西:
SH: count = 1
SH: count = 2
Child 74113 exited with status 0x1000
Child 74114 exited with status 0x2000
SH: count = 3
Child 74115 exited with status 0x3000
SH: count = 4
Child 74116 exited with status 0x4000
Count = 4
这表明信号处理程序在循环期间的不同时间被调用。 BSD 信号处理程序(以及 macOS 或 Darwin 在某种程度上基于 BSD)倾向于重新启动系统调用而不是中断。因此,我看到的不一定是您在不同平台上看到的。
例如,在 Ubuntu 16.04 LTS VM 中运行,我得到了输出:
SH: count = 1
Child 13310 exited with status 0x4000
Child 13309 exited with status 0x3000
Child 13308 exited with status 0x2000
Child 13307 exited with status 0x1000
Count = 1
然而,信号处理程序的另一种改编——重置信号处理程序中的信号处理程序,因为signal()设置的处理程序的传统(非BSD)行为是在处理程序函数之前重置默认值如果您忽略来自write() 的返回值,则会检查自Linux 发出警告以来写入的字节数:
static void handler(int sig)
{
//assert(sig == SIGCHLD);
signal(sig, handler);
count++;
char s[2] = { count + '0', '\n' };
int nb = write(1, "SH: count = ", sizeof("SH: count = ")-1);
assert(nb == sizeof("SH: count = ")-1);
nb = write(1, s, 2);
assert(nb == 2);
}
然后输出变成:
SH: count = 1
Child 13838 exited with status 0x4000
SH: count = 2
Child 13837 exited with status 0x3000
SH: count = 3
Child 13836 exited with status 0x2000
SH: count = 4
Child 13835 exited with status 0x1000
Count = 4
因此,如您所见,您看到的结果取决于您运行代码的平台,以及 handler() 函数的确切编写方式。