为何设置SA_RESETHAND标志的C代码会响应两次SIGUSR1信号?
嘿,我来帮你捋清楚这个问题哈!
首先,你可能对SA_RESETHAND标志的生效时机理解错了——它不是在自定义信号处理函数执行前就移除处理逻辑,而是要等处理函数执行完毕、正常返回之后,才会把该信号的处理动作恢复成系统默认的SIG_DFL。这是第一个容易踩的坑。
那为什么你的代码会响应两次信号呢?主要有两个常见原因:
第一,信号处理期间的阻塞逻辑没做好
你在声明struct sigaction sa的时候,直接设置了sa_handler和sa_flags,但没初始化sa_mask成员。这个成员是用来指定信号处理期间需要阻塞的信号集合,如果你不初始化它,里面会是随机的垃圾值。要是刚好这个随机值里没包含SIGUSR1,那在第一次处理信号的过程中,操作系统不会阻塞新的SIGUSR1信号——这时候如果再收到同一个信号,就会直接再次触发你的自定义处理函数,自然就出现了“响应两次”的情况。第二,信号递送的时序问题
如果你是连续发送了两次SIGUSR1信号,第一次信号触发自定义处理后,第二次信号会被操作系统挂起(如果sa_mask正确包含了SIGUSR1的话)。等第一次处理函数执行完,处理动作恢复成默认,这时候挂起的第二次信号会被递送,执行默认动作(比如终止程序)。要是你只看signal_handled变量的变化,可能会误以为是自定义处理函数被执行了两次,但其实第二次是系统默认动作在生效。
要解决这个问题,你只需要把sigaction结构体的所有成员都正确初始化就行,给你修正后的代码参考:
#include <unistd.h> #include <stdio.h> #include <signal.h> #include <string.h> int signal_handled = 0; void sighandler(int sig) { signal_handled++; printf("信号已处理!计数:%d\n", signal_handled); } int main(int args, char** argv) { struct sigaction sa; // 先清空整个结构体,避免未初始化的垃圾值干扰 memset(&sa, 0, sizeof(sa)); sa.sa_handler = &sighandler; sa.sa_flags = SA_RESETHAND; // 显式添加SIGUSR1到阻塞掩码,处理期间不再接收该信号 sigaddset(&sa.sa_mask, SIGUSR1); if (sigaction(SIGUSR1, &sa, NULL) == -1) { perror("sigaction 调用失败"); return 1; } printf("等待SIGUSR1信号...\n"); // 等待信号触发 while (signal_handled < 1) { pause(); } printf("自定义处理函数已执行一次,现在已恢复为默认处理动作\n"); // 此时再发SIGUSR1会触发默认终止动作 pause(); return 0; }
简单来说,核心就是:SA_RESETHAND是处理完才复位,一定要正确初始化sigaction的所有成员,尤其是信号阻塞掩码,这样就能符合你的预期啦!
内容来源于stack exchange

