APUE所述早期UNIX信号特性与Ubuntu 22.04测试结果不符问题
问题解答
你遇到的情况是因为不同UNIX/Linux系统中signal()函数的实现行为存在历史差异:
早期UNIX的
signal()行为:正如APUE提到的早期版本(比如V7 UNIX),signal()注册信号处理函数后,当信号触发并执行处理函数时,系统会自动将该信号的处理动作重置为默认值(SIG_DFL)。所以当时必须在信号处理函数内部重新调用signal()来再次注册,否则下一次收到该信号时就会执行默认动作(比如终止进程)。现代Linux的
signal()实现:Ubuntu 22.04使用的是glibc库,其中的signal()函数是POSIX兼容的改进实现,它底层通过sigaction()来实现,并且默认不会设置SA_RESETHAND标志(这个标志的作用就是触发信号后重置处理动作)。因此,每次收到信号时,处理函数会被重复调用,不会自动重置为默认行为。
如果你想模拟早期UNIX的signal()行为,可以直接使用sigaction()并显式设置SA_RESETHAND标志,示例代码如下:
#include <unistd.h> #include <stdio.h> #include <signal.h> void sh(int signo) { printf("signo=%d\n", signo); } int main(int argc, char *argv[]) { struct sigaction act; act.sa_handler = sh; sigemptyset(&act.sa_mask); act.sa_flags = SA_RESETHAND; // 设置该标志,触发后重置处理动作 sigaction(SIGUSR1, &act, NULL); while (1) pause(); return 0; }
编译运行这个代码后,第一次发送SIGUSR1会执行sh()函数,第二次发送该信号就会触发默认动作(终止进程),和早期UNIX的行为一致。
注意:信号处理函数中使用
printf要谨慎,因为printf属于非可重入函数,在信号处理场景下可能出现未定义行为,生产环境中建议使用write()这类可重入函数来输出信息。
内容的提问来源于stack exchange,提问作者user14892865
相关产品推荐
相关产品推荐

