You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

尝试复现ASAN假阳性时发现连续调用alarm()第二个信号未触发,求原因

为什么第二次alarm()的信号从未触发?

这个问题的核心坑点在于**longjmp()不会自动恢复信号掩码**——咱们一步步拆解来理清楚:

问题的来龙去脉

当SIGALRM信号触发并进入处理函数handle_timeout时,系统会自动把SIGALRM加入进程的信号掩码(也就是暂时阻塞这个信号),这是系统的默认行为,目的是防止处理过程中再次收到同信号导致嵌套调用,和你看到的GNU文档描述一致。

但你用longjmp(jump_buffer, 1)直接跳回了test()里的setjmp返回点,这就绕过了信号处理函数的正常返回流程:

  • 正常情况下,信号处理函数执行完毕返回时,系统会自动恢复之前的信号掩码(解除SIGALRM的阻塞);
  • 但longjmp是“强行跳转”,完全跳过了这个恢复步骤,导致SIGALRM一直处于被阻塞的状态!

所以第二次调用alarm(2)确实会在2秒后发送SIGALRM信号,但进程的信号掩码还挡着它,信号被挂起无法触发处理函数,你的程序就卡在f()的死循环里永远跑下去了。

修复方案

有两种可靠的解决方式,推荐第一种:

方案1:使用sigsetjmp和siglongjmp(推荐)

这对函数是专门为信号场景设计的,会自动保存和恢复信号掩码。只需要把原来的jmp_buf、setjmp、longjmp替换成对应的信号版本即可:

#include <setjmp.h>
#include <signal.h>
#include <unistd.h>

static sigjmp_buf jump_buffer; // 替换为sigjmp_buf

void f() { while(1) {}; }

void handle_timeout(int) { 
    siglongjmp(jump_buffer, 1); // 替换为siglongjmp
}

void test() { 
    // sigsetjmp第二个参数传1,表示保存当前信号掩码
    if (sigsetjmp(jump_buffer, 1) == 0) { 
        f(); 
    } 
}

int main() {
    signal(SIGALRM, handle_timeout);
    alarm(2);
    test();
    
    signal(SIGALRM, handle_timeout);
    alarm(2);
    test();
    
    return 0;
}

方案2:手动恢复信号掩码

如果坚持用longjmp,可以在跳转前手动解除SIGALRM的阻塞:

void handle_timeout(int sig) {
    sigset_t mask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGALRM);
    // 手动解除SIGALRM的阻塞
    sigprocmask(SIG_UNBLOCK, &mask, NULL);
    longjmp(jump_buffer, 1);
}

不过这种方式需要你自己处理信号掩码的细节,不如第一种方案省心可靠。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:51:34