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
相关产品推荐
相关产品推荐

