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

如何捕获SIGINT信号以中断waitpid()函数?

实现SIGINT打断waitpid并继续执行的方案

刚好之前做过类似的需求,核心要抓住信号会打断阻塞的系统调用这个特性——waitpid()就是典型的会被信号打断的函数,当它被信号中断时会返回-1,同时errno被设置为EINTR。我们可以利用这一点来实现你的需求,具体步骤如下:

1. 编写可靠的SIGINT信号处理函数

信号处理函数不需要复杂逻辑,甚至可以是空函数,只要它被注册,就能触发waitpid()的中断。不过为了友好性,你可以加个提示信息,但要注意信号处理函数里尽量用异步信号安全的函数(比如write(),而不是printf(),因为printf不是异步安全的,极端场景可能出问题):

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

void sigint_handler(int sig) {
    // 用write输出提示,确保异步安全
    write(STDOUT_FILENO, "\n收到CTRL+C,正在退出子进程等待...\n", 36);
}

2. 用sigaction注册信号处理函数(别用signal()!)

signal()函数的行为在不同Unix-like系统上不一致,推荐用sigaction()来注册信号处理函数,关键是不要设置SA_RESTART标志——这个标志会让被信号打断的系统调用自动重启,那样waitpid()就不会返回-1,也就没法退出等待了:

#include <errno.h>
#include <sys/wait.h>
#include <stdio.h>

int main() {
    struct sigaction sa;
    sa.sa_handler = sigint_handler;
    sigemptyset(&sa.sa_mask);  // 处理SIGINT期间不屏蔽其他信号(可按需调整)
    sa.sa_flags = 0;           // 核心:不要加SA_RESTART!

    if (sigaction(SIGINT, &sa, NULL) == -1) {
        perror("sigaction注册信号失败");
        return 1;
    }

    // 先fork一个子进程模拟场景
    pid_t child_pid = fork();
    if (child_pid == -1) {
        perror("fork子进程失败");
        return 1;
    } else if (child_pid == 0) {
        // 子进程:模拟长时间任务,比如sleep 10秒
        sleep(10);
        return 0;
    }

    // 父进程等待子进程的逻辑
    int status;
    pid_t wait_ret;
    while (1) {
        wait_ret = waitpid(child_pid, &status, 0);
        if (wait_ret == -1) {
            if (errno == EINTR) {
                // 捕获到EINTR,说明waitpid被SIGINT打断了,退出循环继续执行
                printf("waitpid已退出,开始执行后续程序逻辑...\n");
                break;
            } else {
                // 其他waitpid错误,比如子进程不存在等
                perror("waitpid执行出错");
                return 1;
            }
        } else {
            // 子进程正常退出,处理退出状态
            if (WIFEXITED(status)) {
                printf("子进程正常退出,退出码:%d\n", WEXITSTATUS(status));
            } else if (WIFSIGNALED(status)) {
                printf("子进程被信号终止,信号编号:%d\n", WTERMSIG(status));
            }
            break;
        }
    }

    // 这里就是你要继续执行的正常逻辑
    printf("程序继续运行,完成剩余操作...\n");
    return 0;
}

关键注意事项

  • 禁止SA_RESTART标志:这是最容易踩的坑,如果设置了这个标志,waitpid()被信号打断后会自动重启,永远不会返回-1,也就无法退出等待。
  • 异步信号安全函数:信号处理函数里只能调用异步信号安全的函数,比如write()、_exit()等,避免用printf()、malloc()这类非安全函数。
  • 可选:终止子进程:如果你希望按下CTRL+C时不仅打断waitpid(),还要终止子进程,可以在信号处理函数里给子进程发信号(比如kill(child_pid, SIGTERM)),但要注意把child_pid声明为全局变量或者静态变量,因为信号处理函数无法直接访问局部变量。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:03:51