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

C语言中递归调用alarm()实现子进程定时打印异常问题

问题分析与解决

我尝试创建多个子进程,让它们每隔几秒触发SIGALRM信号并向标准输出打印消息。不想用无限while循环,于是尝试在SIGALRM信号处理函数里递归调用alarm()和pause(),但无法正常输出预期消息;改用无限while循环包裹pause()时功能正常。

相关代码

child.c 核心代码

void handle_sigalarm(int sig){
    now = 0;
    time(&now);
    printf("[CHILD] | [TIME = %ld] | [PID = %d] | [GATE = %d]\n", (long int)(now - start), getpid(), GATE);
    alarm(2);
    pause();
}


int main(int argc, char **argv){ 

    signal_handling(); // 处理所有信号的函数,实现见下方

    printf("[CHILD] | [TIME = %d] | [PID = %d] | [GATE = %d]\n", 0, getpid(), GATE);

    time(&start);

    alarm(2);
    pause();

    // while(1){
    //     pause();
    // }

    return 0;
}

信号处理函数实现

void signal_handling(){
    struct sigaction sa1;
    struct sigaction sa2;
    struct sigaction sa3;
    struct sigaction sal;

    sa1.sa_handler = handle_sigterm;
    sa2.sa_handler = handle_sigusr1;
    sa3.sa_handler = handle_sigusr2;
    sigemptyset(&sal.sa_mask);
    sal.sa_handler = handle_sigalarm;
    sal.sa_flags = SA_RESTART;

    sigaction(SIGTERM, &sa1, NULL);
    sigaction(SIGUSR1, &sa2, NULL);
    sigaction(SIGUSR2, &sa3, NULL);
    sigaction(SIGALRM, &sal, NULL);
}

问题原因

问题出在信号处理函数里的pause()调用:

  • 当信号处理函数执行时,当前触发的信号(这里是SIGALRM)会被自动加入进程的信号掩码,也就是说在handle_sigalarm执行期间,新的SIGALRM信号会被挂起,无法触发新的信号处理。
  • 你在handle_sigalarm里调用alarm(2)设置了下一个定时器,紧接着调用pause()等待信号,但此时SIGALRM被屏蔽,定时器触发的SIGALRM会被挂起,而pause()会一直等待其他未被屏蔽的信号,导致进程卡在信号处理函数里,无法继续处理后续的SIGALRM,自然不会再打印消息。
  • 另外,递归调用pause()还会不断占用栈空间,存在栈溢出的风险。

解决方法

不要在信号处理函数里调用pause(),只需要在处理函数中设置下一个定时器,让main函数保持等待状态即可:

修改handle_sigalarm函数:

void handle_sigalarm(int sig){
    now = 0;
    time(&now);
    printf("[CHILD] | [TIME = %ld] | [PID = %d] | [GATE = %d]\n", (long int)(now - start), getpid(), GATE);
    alarm(2); // 只设置下一个定时器,不需要调用pause()
}

然后在main函数里用无限循环等待信号(这是最直观的实现方式):

int main(int argc, char **argv){ 
    signal_handling();

    printf("[CHILD] | [TIME = %d] | [PID = %d] | [GATE = %d]\n", 0, getpid(), GATE);

    time(&start);

    alarm(2);
    while(1){
        pause();
    }

    return 0;
}

如果确实不想用while循环,可以用sigsuspend配合空掩码实现永久等待,逻辑和while循环一致:

int main(int argc, char **argv){ 
    signal_handling();

    printf("[CHILD] | [TIME = %d] | [PID = %d] | [GATE = %d]\n", 0, getpid(), GATE);

    time(&start);

    alarm(2);
    sigset_t mask;
    sigemptyset(&mask);
    while(1){
        sigsuspend(&mask); // 等待任何未被屏蔽的信号
    }

    return 0;
}

这样修改后,每次SIGALRM触发时,信号处理函数打印消息并设置下一个定时器,然后返回,main函数的pause()/sigsuspend()会继续等待下一个信号,就能实现每隔2秒打印一次消息的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 07:37:39