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

UNIX C编程:替换system()为exec族的子进程信号处理问题

核心问题分析

你两版代码的问题出在进程调度和状态等待逻辑上:

  • 第一版用system()的缺陷:system()内部会独立fork shell进程执行命令,返回的状态值是中间shell进程的退出状态,无法准确获取xterm进程被信号终止的真实状态;同时父进程没有对子进程做回收,父子进程共享进程状态数组会出现内存拷贝导致的状态不同步。
  • 第二版串行等待的缺陷:每fork一个子进程就立刻调用阻塞式wait(),父进程必须等当前子进程退出才能继续fork下一个,自然无法并行打开多个终端;另外外层循环会反复给所有标记为存活的命令重新fork进程,没有记录已启动的子进程ID,会重复生成冗余进程。
实现逻辑

采用两层进程结构解决上述问题:

  • 父进程一次性为2-4个传入命令分别fork独立的监控子进程,之后用非阻塞方式回收所有退出的子进程,不阻塞等待单个子进程,保证所有终端并行启动。
  • 每个监控子进程独立负责对应xterm的生命周期:循环拉起xterm,若xterm因用户按回车正常退出则自动重启,若xterm被信号终止则打印提示后直接退出,不再重启。
  • 调用xterm时显式通过sh -c执行命令+等待回车的逻辑,避免shell命令拼接的转义问题,保证退出状态判断准确。
可运行代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>

int main(int argc, char **argv) {
    int cmd_num = argc - 1;
    // 入参校验
    if (cmd_num < 2 || cmd_num > 4) {
        fprintf(stderr, "Usage: %s cmd1 cmd2 [cmd3] [cmd4]\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    pid_t monitor_pids[cmd_num];
    memset(monitor_pids, 0, sizeof(monitor_pids));

    // 为每个命令启动独立监控子进程
    for (int i = 0; i < cmd_num; i++) {
        pid_t pid = fork();
        if (pid < 0) {
            perror("fork monitor process failed");
            exit(EXIT_FAILURE);
        }
        if (pid == 0) {
            // 监控子进程逻辑
            const char *target_cmd = argv[i + 1];
            char exec_buf[2048];
            while (1) {
                pid_t xterm_pid = fork();
                if (xterm_pid < 0) {
                    perror("fork xterm process failed");
                    exit(EXIT_FAILURE);
                }
                if (xterm_pid == 0) {
                    // 拼接执行命令:运行目标命令后提示按回车,等待用户输入
                    snprintf(exec_buf, sizeof(exec_buf), 
                            "%s; echo \"Command finished, press Enter to close window\"; read", 
                            target_cmd);
                    // 启动xterm,通过sh -c执行拼接好的命令
                    execl("/usr/bin/xterm", "xterm", "-e", "sh", "-c", exec_buf, (void*)NULL);
                    perror("exec xterm failed");
                    exit(EXIT_FAILURE);
                }
                // 等待当前xterm进程退出
                int xterm_status;
                waitpid(xterm_pid, &xterm_status, 0);
                if (WIFSIGNALED(xterm_status)) {
                    printf("Command '%s' terminal killed by signal %d, will not restart\n",
                           target_cmd, WTERMSIG(xterm_status));
                    exit(EXIT_SUCCESS);
                }
                if (WIFEXITED(xterm_status)) {
                    printf("Command '%s' terminal closed normally, restarting...\n", target_cmd);
                }
            }
        } else {
            monitor_pids[i] = pid;
        }
    }

    // 父进程非阻塞回收所有监控子进程
    int alive_cnt = cmd_num;
    while (alive_cnt > 0) {
        int status;
        pid_t exited_pid = waitpid(-1, &status, WNOHANG);
        if (exited_pid > 0) {
            for (int i = 0; i < cmd_num; i++) {
                if (monitor_pids[i] == exited_pid) {
                    monitor_pids[i] = 0;
                    alive_cnt--;
                    break;
                }
            }
        }
        // 短休眠避免空转占满CPU
        usleep(100000);
    }

    printf("All terminals closed, program exit\n");
    return 0;
}
编译运行说明
  • 编译命令:gcc proc_monitor.c -o proc_monitor
  • 运行示例:./proc_monitor who date 会同时打开两个xterm,分别执行who、date命令,按回车会自动重启对应终端,点窗口关闭按钮或发送信号杀死终端会打印提示不再重启,所有终端都被信号杀死后程序自动退出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 22:12:25