信号不能在execv()之间正确重新启用

c47*_*57p 3 c linux signals

我正在为我正在开发的Linux发行版编写一个系统关键程序.它需要在接收某些信号时重新启动,以尽量避免崩溃.问题是,重启后,我无法重新启用该信号.也就是说,信号不能被接收两次.在execv()之后,当新进程调用signal()来设置信号时,返回SIG_DFL.每次.即使我连续两次调用它 - 表明它从未设置在第一位.是否有一些奇怪的旗帜从原始过程中被遗留下来?

Dav*_*veR 9

你实际上是在尝试以递归方式处理信号这一事实.

signal()用于注册信号处理程序时,该信号编号被阻塞,直到信号处理程序返回 - 实际上,当调用信号处理程序时,内核/ libc会阻塞该信号编号,并在信号处理程序返回后解除阻塞.因为你永远不会从信号处理程序返回(而是你execl一个新的二进制文件),SIGUSR1所以保持阻塞状态,因此第二次没有被捕获.

通过/proc/</pid>/status在发送第一个之前和之后进行检查可以看出这一点SIGUSR1.

之前:

$ cat /proc/<pid>/status | grep -E "Sig(Cgt|Blk)"
SigBlk: 0000000000000000
SigCgt: 0000000000000200
Run Code Online (Sandbox Code Playgroud)

后:

$ cat /proc/<pid>/status | grep -E "Sig(Cgt|Blk)"
SigBlk: 0000000000000200
SigCgt: 0000000000000200
Run Code Online (Sandbox Code Playgroud)

注意,SigCgt指示信号10​​被注册(该数字是位域;设置第10位,相当于SIGUSR1,参见man signal(7)数字).SigBlkSIGUSR发送到您的进程之前是空的,但在发送它包含的信号之后SIGUSR1.

您有两种方法可以解决这个问题:

一个).手动解锁SIGUSR之前调用execlsighandler:

sigset_t sigs;
sigprocmask(0, 0, &sigs);
sigdelset(&sigs, SIGUSR1);
sigprocmask(SIG_SETMASK, &sigs);
Run Code Online (Sandbox Code Playgroud)

B).sigactionSA_NODEFER标志一起使用而不是signal注册信号处理程序.这样可以防止SIGUSR1在信号处理程序中被阻塞:

struct sigaction act;
act.sa_handler = signalhandler;
act.sa_mask = 0;
act.sa_flags = SA_NODEFER;
sigaction(SIGUSR1, &act, 0);
Run Code Online (Sandbox Code Playgroud)