我想了解这段代码,
int count =0;
void handler(int sig){
count++;
}
int main(){
signal(SIGCHILD,handler);
for(int i=0;i<4;i++){
if(!fork()){
exit(0 );
}
}
while(wait(NULL) ! = -1){
}
print(count)
}
所以,我希望因为有4个SIGCHILD信号,所以处理器应该被调用四次。但是,由于我们可以有一个最大的待处理信号,因此可能会丢弃一些信号,而计数可能不会是四个。
但是,如果父进程在单个子进程退出之前调用wait,那么SIGCHILD信号将如何处理?在这种情况下,计数是4吗?
在这种情况下,SIGCHILD,处理程序代码和父进程等待之间的流程如何?
答案 0 :(得分:0)
有点清理的代码版本变成了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;
}
在运行带有GCC 6.3.0的macOS Sierra 10.12.4的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
而不是其中一些1
s:< / p>
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()
函数的确切编写方式。