You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何避免快速连续发送的SIGUSR1信号被操作系统忽略?

解决重复SIGUSR1信号被合并丢失的问题

这个问题我之前调试代码时也碰到过,核心根源很好理解:你用的SIGUSR1属于不可靠信号(POSIX标准里的非实时信号),这类信号的特性是操作系统不会为它们排队——如果多个相同信号在父进程还没处理完前一个的间隙到达,系统只会保留一个,剩下的直接丢弃,这就是为什么你偶尔只会看到一次handler触发。

现在要解决这个问题,又不想换不同信号、也不想给子进程加延迟,有两个非常靠谱的方案:

方案1:改用实时信号(最推荐)

POSIX定义的实时信号(范围是SIGRTMINSIGRTMAX)是支持排队的——每一个发送的实时信号都会被操作系统保存下来,直到进程处理它们,绝对不会被合并。你只需要把代码里的SIGUSR1替换成某个实时信号就行,比如最常用的SIGRTMIN

修改后的关键代码片段

// 替换SIGUSR1为实时信号SIGRTMIN
sigaction(SIGRTMIN, &sigact, NULL);

// 子进程发送信号时也对应改成SIGRTMIN
sigqueue(getppid(), SIGRTMIN, signalValueInt);

这样不管两个子进程发送信号的间隔有多短,父进程的handler都会被触发两次,因为每个实时信号都会被系统排队处理,不会丢失。

方案2:用sigwaitinfo同步等待信号

如果你不想改用实时信号,还可以让父进程主动等待信号,而不是依赖异步的信号handler。这种方式能逐个接收pending的信号,完全不会错过。具体做法如下:

修改后的main函数核心逻辑

int main() {
 pid_t ambulance1 = 0;
 pid_t ambulance2 = 0;
 sigset_t sigset;
 sigemptyset(&sigset);
 sigaddset(&sigset, SIGUSR1);
 
 // 先阻塞SIGUSR1,让信号进入pending状态,不会触发异步handler
 sigprocmask(SIG_BLOCK, &sigset, NULL);

 ambulance1 = fork();
 if(ambulance1 > 0) {
 ambulance2 = fork();
 if(ambulance2 > 0) {
 // 父进程:主动等待两个信号,确保都收到
 struct siginfo info;
 for(int i=0; i<2; i++){
     sigwaitinfo(&sigset, &info);
     // 这里直接执行原来handler里的逻辑,比如打印你需要的<Text>
     printf("Received signal from ambulance %d\n", info.si_pid);
 }
 int status;
 waitpid(ambulance1, &status, 0);
 waitpid(ambulance2, &status, 0);
 printf("HQ went home!\n");
 }
 }
 if(ambulance1 == 0 || ambulance2 == 0) {
 union sigval signalValueInt;
 signalValueInt.sival_int = SIG_INT;
 sigqueue(getppid(), SIGUSR1, signalValueInt);
 printf("Ambulance[%d] ended.\n", getpid());
 }
 return 0;
}

这个方案的原理是:父进程先把SIGUSR1阻塞,这样子进程发送的信号会被标记为pending(未处理),不会被系统合并。然后父进程用sigwaitinfo主动等待信号,每次调用都会返回一个pending的信号,循环两次就能确保收到两个子进程的信号,完美解决丢失问题。


内容的提问来源于stack exchange,提问作者vapandris

火山引擎 最新活动