C 在处理程序中阻止新信号
我有一个管理子进程(fork、execve)的父进程。我在父级中创建了一个处理程序来捕获来自子级的SIGCHLD信号,以便调用C 在处理程序中阻止新信号,c,linux,multithreading,signals,C,Linux,Multithreading,Signals,我有一个管理子进程(fork、execve)的父进程。我在父级中创建了一个处理程序来捕获来自子级的SIGCHLD信号,以便调用waitpid(),并采取适当的操作,例如重新启动子级 我从sigaction()的手册页面了解到,在信号处理程序中,默认情况下,相同类型的其他信号将被阻止。我当然希望这种行为,所以我决定测试它 我在信号处理程序的末尾放了一个sleep(我自己的实现使用了clock\u nanosleep()在一个循环中,该循环在中断时恢复),并向孩子发送了一个SIGINT。这使它及时退
waitpid()
,并采取适当的操作,例如重新启动子级
我从sigaction()
的手册页面了解到,在信号处理程序中,默认情况下,相同类型的其他信号将被阻止。我当然希望这种行为,所以我决定测试它
我在信号处理程序的末尾放了一个sleep(我自己的实现使用了clock\u nanosleep()
在一个循环中,该循环在中断时恢复),并向孩子发送了一个SIGINT。这使它及时退出并将SIGCHLD发送给家长。我把事实记录下来,开始睡10秒钟。现在,我给新孩子发送了另一个信号(sighandler第一次重新启动了它),并惊讶地看到另一个日志和睡眠发生了
这怎么可能?当我使用调试器连接到父线程时,它清楚地显示两个不同的线程在调用我的信号处理程序时被中断,它们现在都处于睡眠状态。如果这种情况持续下去,我的线程就会用完
我理解将长时间睡眠放入信号处理器是一件愚蠢的事情,但它确实说明了这一点;我希望在/proc/[PID]/status中看到第二个标记为挂起的信号,但是它被传递了
以下是我代码的相关部分:
设置SIGCHLD处理程序:
typedef struct SigActType {
struct sigaction act;
int retval;
void (*func)(int);
}SigActType;
static SigActType sigActList[64];
public void setChildHandler(void (*func)(int)) {
SigActType *sat = &sigActList[SIGCHLD];
sat->act.sa_sigaction = sigchldHandler;
sigemptyset(&sat->act.sa_mask);
sigaddset (&sat->act.sa_mask, SIGTERM);
sigaddset (&sat->act.sa_mask, SIGINT);
sigaddset (&sat->act.sa_mask, SIGCHLD);
sat->act.sa_flags = SA_SIGINFO;
sat->retval = 0;
sat->func = func;
sigaction(SIGCHLD, &sat->act, NULL);
}
static void sigchldHandler(int sig, siginfo_t *si, void *thing) {
SigActType *sat = &sigActList[SIGCHLD];
if (sat->func) {
sat->func(si->si_pid);
}
}
使用这个:
int main(int argc, char **argv) {
setChildHandler(manageChildSignals);
...
}
static void manageChildSignals(int d) {
if ((pid = waitpid(-1, &stat, WAIT_MYPGRP)) > 0) {
... restart child if appropriate
}
printf("start of pause...\n");
mySleep(10);
printf("end of pause...\n");
}
Stdout清楚地表明:
(when I type kill -2 [PID]
start of pause
(when the new child is started and I type kill -2 [NEWPID]
start of pause
...10 seconds slide past...
end of pause
end of pause
我不明白为什么会发生这种情况。如您所见,我甚至在sigaction()
的块掩码中添加了SIGCHLD,以鼓励它做正确的事情
欢迎指点
默认情况下,相同类型的信号将被阻止
是的,但仅针对从调用的线程
来自(粗体我强调):
sa_mask指定了应阻止的信号屏蔽(即。,
添加到包含信号处理程序的线程的信号掩码
在执行信号处理程序期间调用
由于信号处理是每个进程进行的,因此任何其他未阻塞相关信号的线程都可能接收到该信号,也就是说,被中断并对其进行处理
如果这种行为不是您想要的,您可能应该修改程序处理信号的方式的设计,按照默认方式,每个线程的所有信号都被阻塞,并且只有一个特定线程的信号接收未被阻塞
更新:
子线程从父线程继承信号掩码
如果信号处理仅由一个特定线程完成,则在创建任何其他线程之前,让主线程阻止所有信号。然后创建一个特定的线程来执行信号处理,并让该线程解锁要处理的信号。这个概念还允许像每个信号一个线程这样的模型
在多线程环境中,用于在每个线程基础上屏蔽信号
请注意,
sigprocmask()
在多线程进程中的行为是未指定的,请使用pthread\u sigmask()
。mysleep做什么?它是使用clock\u nanosleep()实现的sleep()
在一个循环中,当它返回EINTR时,它会休眠正确的时间,即使被中断。哦,你正在使用多个线程。抱歉;我应该说清楚的。是的,main()
设置信号处理程序,然后工作线程进入睡眠状态。其他线程做这项工作。在这种情况下,每个线程都有资格被强制跳转到信号。“仅针对调用sigaction的线程”?这也许可以解释。我的其他线程可能会被中断来处理这个信号?只是不是我称之为sigaction的那个“主要”的?是的。但我认为处理信号的不同线程不会导致问题。我认为sigaction()
适用于该流程!用于“更新”。是的,我想到了这一点,并在子进程启动时将它们放回原位。@jrichemont:is就是这样做的,处理是为进程的所有线程安装的。然而,对于实际接收到信号的线程,会进行Making。掩码是在每个线程的基础上完成的。请参阅我的答案中关于pthread_sigmask()的补充。