ArchLinux上Kill系统调用异常问题求助
问题原因与代码错误分析
核心问题点
signal函数的行为不确定性
不同Linux发行版对signal的实现遵循不同标准:BSD风格会保留信号处理函数,而System V风格在信号触发后会自动恢复为默认处理。新版本内核/发行版可能默认采用System V行为,导致你的代码中handler里重新注册信号的操作赶不上后续信号,子进程对SIGUSR1的处理变回默认(忽略),因此没有输出。子进程信号注册的竞争条件
子进程在while循环里每次pause前都重新注册信号,存在时间窗口:父进程发送的SIGUSR1可能在子进程调用signal前到达,此时信号被默认处理(忽略),后续pause一直阻塞,直到下一个信号,但此时处理函数可能已被恢复默认,依旧无输出。非异步信号安全函数的调用
printf不属于异步信号安全函数,在信号处理函数中调用会触发未定义行为。不同内核/发行版的标准库对这种违规操作的容错性不同,这也是输出差异的原因之一。退出信号不匹配
父进程最后发送的是SIGTERM,但子进程只注册了SIGUSR1和SIGUSR2的处理函数,SIGTERM的默认行为是直接终止进程,子进程没有机会执行输出逻辑就退出了。
修正后的代码示例
#include <stdio.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> void handler(int sig) { char msg[64]; int len = snprintf(msg, sizeof(msg), "Received %d signal in %d!\n", sig, getpid()); // 使用异步信号安全的write函数输出 write(STDOUT_FILENO, msg, len); if(sig == SIGUSR2) _exit(0); // 信号处理函数中用_exit而非exit,避免缓冲区刷新问题 } int main() { pid_t child = fork(); if(child == 0) { struct sigaction sa; sa.sa_handler = handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; // 让被信号打断的系统调用自动重启 // 用sigaction注册信号,行为稳定可预测 sigaction(SIGUSR1, &sa, NULL); sigaction(SIGUSR2, &sa, NULL); while(1) { pause(); } } else { for(int i = 0; i < 10; i++) { printf("Sending SIGUSR1 signal to %d\n", child); kill(child, SIGUSR1); sleep(1); } printf("Sending SIGUSR2 signal\n"); kill(child, SIGUSR2); // 发送SIGUSR2匹配子进程退出逻辑 wait(NULL); } }
关键修正说明
- 用
sigaction替代signal:sigaction是POSIX标准接口,行为一致,不会因发行版/内核版本产生差异,同时SA_RESTART可以让pause在被信号打断后自动重启,避免阻塞。 - 替换
printf为write:write是异步信号安全函数,消除未定义行为。 - 子进程仅注册一次信号处理函数:彻底解决循环注册导致的竞争问题。
- 父进程发送SIGUSR2而非SIGTERM:匹配子进程的退出逻辑,确保子进程正常执行退出前的输出。
内容的提问来源于Stack Exchange,提问作者Setu
相关产品推荐
相关产品推荐

