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

C语言如何向运行超时的子进程定时发送SIGABRT信号

C语言多子进程运行时长监控及超时终止实现

核心实现思路

  • 父进程维护子进程信息列表,每个条目存储子进程PID、启动时间戳、允许的最大运行时长
  • 使用CLOCK_MONOTONIC单调时钟计算运行时长,不受系统时间修改的影响
  • 父进程通过非阻塞的waitpid轮询回收已退出的子进程,同时核对运行中进程的时长,超时则发送SIGABRT信号
  • 及时回收退出的子进程资源,避免产生僵尸进程

完整可运行示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <time.h>
#include <signal.h>
#include <string.h>

#define MAX_CHILD_NUM 3
#define DEFAULT_TIMEOUT 5 // 默认超时时间5秒

// 子进程信息结构体
typedef struct {
    pid_t pid;
    struct timespec start_time;
    int timeout_sec;
    int is_running;
} ChildProcess;

int main() {
    ChildProcess children[MAX_CHILD_NUM] = {0};
    int child_count = 0;

    // 启动3个测试子进程,分别执行sleep 10、sleep 3、sleep 8
    int sleep_times[] = {10, 3, 8};
    for (int i = 0; i < MAX_CHILD_NUM; i++) {
        pid_t pid = fork();
        if (pid < 0) {
            perror("fork failed");
            exit(EXIT_FAILURE);
        } else if (pid == 0) {
            // 子进程逻辑
            char sleep_str[16];
            snprintf(sleep_str, sizeof(sleep_str), "%d", sleep_times[i]);
            execlp("sleep", "sleep", sleep_str, NULL);
            perror("execlp failed");
            exit(EXIT_FAILURE);
        } else {
            // 父进程记录子进程信息
            children[child_count].pid = pid;
            clock_gettime(CLOCK_MONOTONIC, &children[child_count].start_time);
            children[child_count].timeout_sec = DEFAULT_TIMEOUT;
            children[child_count].is_running = 1;
            child_count++;
            printf("启动子进程 %d,预设休眠时长 %d 秒,超时阈值 %d 秒\n", 
                    pid, sleep_times[i], DEFAULT_TIMEOUT);
        }
    }

    // 父进程监控循环
    int running_count = child_count;
    while (running_count > 0) {
        // 先回收所有已退出的子进程
        int status;
        pid_t exited_pid;
        while ((exited_pid = waitpid(-1, &status, WNOHANG)) > 0) {
            for (int i = 0; i < child_count; i++) {
                if (children[i].pid == exited_pid && children[i].is_running) {
                    children[i].is_running = 0;
                    running_count--;
                    if (WIFSIGNALED(status)) {
                        printf("子进程 %d 被信号 %d 终止\n", exited_pid, WTERMSIG(status));
                    } else if (WIFEXITED(status)) {
                        printf("子进程 %d 正常退出,退出码 %d\n", exited_pid, WEXITSTATUS(status));
                    }
                }
            }
        }

        // 检查所有运行中的子进程是否超时
        struct timespec now;
        clock_gettime(CLOCK_MONOTONIC, &now);
        for (int i = 0; i < child_count; i++) {
            if (!children[i].is_running) continue;

            long run_sec = now.tv_sec - children[i].start_time.tv_sec;
            if (run_sec >= children[i].timeout_sec) {
                printf("子进程 %d 运行超时(已运行 %ld 秒),发送 SIGABRT 信号\n", 
                        children[i].pid, run_sec);
                kill(children[i].pid, SIGABRT);
            }
        }

        // 休眠100毫秒,降低CPU占用
        struct timespec sleep_interval = {0, 100000000};
        nanosleep(&sleep_interval, NULL);
    }

    printf("所有子进程处理完毕,父进程退出\n");
    return 0;
}

效果说明

编译运行上述代码后会输出类似日志:

启动子进程 12345,预设休眠时长 10 秒,超时阈值 5 秒
启动子进程 12346,预设休眠时长 3 秒,超时阈值 5 秒
启动子进程 12347,预设休眠时长 8 秒,超时阈值 5 秒
子进程 12346 正常退出,退出码 0
子进程 12345 运行超时(已运行 5 秒),发送 SIGABRT 信号
子进程 12347 运行超时(已运行 5 秒),发送 SIGABRT 信号
子进程 12345 被信号 6 终止
子进程 12347 被信号 6 终止
所有子进程处理完毕,父进程退出

其中信号6就是SIGABRT,符合预期效果。

优化建议

  • 若子进程数量不固定,可以用链表代替固定数组存储子进程信息,支持动态增删
  • 若需要更高精度的超时控制,可以纳秒为单位计算时间差,调整超时判断逻辑
  • 若父进程需要同时处理其他IO任务,可以改用timerfd+epoll的事件驱动模式,避免轮询带来的CPU开销

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 17:39:02