/* * task54.c — 子进程管理程序 * * 借鉴 sigmask.c 的信号掩码方法管理子进程: * 1. 在操作子进程列表时阻塞 SIGCHLD, 防止信号处理程序并发修改 * 2. 通过 sigwait / signal handler 等待子进程终止 * * 支持命令: * create 创建 n 个子进程, 显示各子进程 PID * kill ... 终止指定 PID 的子进程, 子进程打印 "killed by parent" 后退出 * ps -u 显示当前存活子进程列表 * exit 等待所有子进程结束后退出父进程 */ #include #include #include #include #include #include #include #define MAX_CHILDREN 1024 #define MAX_LINE 256 /* 子进程 PID 列表, 受 SIGCHLD 信号保护 */ static pid_t child_pids[MAX_CHILDREN]; static int child_count = 0; /* ---------- SIGCHLD 信号处理程序 ---------- */ static void sigchld_handler(int sig) { int saved_errno = errno; pid_t pid; int status; /* 回收所有已终止的子进程, 不阻塞 */ while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { printf("[SIGCHLD] 子进程 %d 已终止", pid); if (WIFEXITED(status)) printf(", 退出码=%d", WEXITSTATUS(status)); if (WIFSIGNALED(status)) printf(", 被信号 %d (%s) 终止", WTERMSIG(status), (WTERMSIG(status) == SIGTERM) ? "SIGTERM" : ""); printf("\n"); /* 从列表中移除该 PID */ for (int i = 0; i < child_count; i++) { if (child_pids[i] == pid) { child_pids[i] = child_pids[child_count - 1]; child_count--; break; } } } errno = saved_errno; } /* ---------- 子进程执行的代码 ---------- */ static void child_main(void) { /* 安装 SIGTERM 处理: 打印消息后退出 */ struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = SIG_DFL; /* 先恢复默认以便后续操作 */ sigemptyset(&sa.sa_mask); /* 子进程: 等待 SIGTERM 信号后打印信息并退出 */ sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGTERM); sigprocmask(SIG_BLOCK, &mask, NULL); /* 阻塞 SIGTERM, 用 sigwait 同步等待 */ int sig; sigwait(&mask, &sig); /* 等待 SIGTERM */ printf("killed by parent\n"); exit(100); } /* ---------- 创建子进程 ---------- */ static void cmd_create(int n) { sigset_t block_mask, old_mask; /* 阻塞 SIGCHLD, 防止 handler 并发修改 child_pids */ sigemptyset(&block_mask); sigaddset(&block_mask, SIGCHLD); sigprocmask(SIG_BLOCK, &block_mask, &old_mask); int created = 0; for (int i = 0; i < n; i++) { if (child_count >= MAX_CHILDREN) { printf("子进程数已达上限 %d\n", MAX_CHILDREN); break; } pid_t pid = fork(); if (pid < 0) { perror("fork"); break; } if (pid == 0) { /* 子进程: 恢复默认信号掩码, 执行子进程代码 */ sigprocmask(SIG_SETMASK, &old_mask, NULL); child_main(); /* child_main 不会返回 */ } /* 父进程: 记录子进程 PID */ child_pids[child_count++] = pid; created++; } /* 恢复信号掩码 */ sigprocmask(SIG_SETMASK, &old_mask, NULL); if (created > 0) { printf("已创建 %d 个子进程:", created); /* 新创建的 PID 在列表末尾 */ for (int i = child_count - created; i < child_count; i++) printf(" %d", child_pids[i]); printf("\n"); } } /* ---------- 终止指定子进程 ---------- */ static void cmd_kill(int n, pid_t *pids) { for (int i = 0; i < n; i++) { /* 检查 PID 是否在管理列表中 */ int found = 0; for (int j = 0; j < child_count; j++) { if (child_pids[j] == pids[i]) { found = 1; break; } } if (!found) { printf("PID %d 不是当前管理的子进程, 跳过\n", pids[i]); continue; } /* 发送 SIGTERM, 子进程的 sigwait 会捕获并处理 */ if (kill(pids[i], SIGTERM) == 0) { printf("已向子进程 %d 发送终止信号\n", pids[i]); } else { perror("kill"); } } /* SIGCHLD handler 会自动回收并清理列表 */ } /* ---------- 显示当前子进程列表 ---------- */ static void cmd_ps(void) { sigset_t block_mask, old_mask; /* 阻塞 SIGCHLD, 安全读取列表 */ sigemptyset(&block_mask); sigaddset(&block_mask, SIGCHLD); sigprocmask(SIG_BLOCK, &block_mask, &old_mask); if (child_count == 0) { printf("当前无子进程\n"); } else { printf("当前子进程 (%d 个):", child_count); for (int i = 0; i < child_count; i++) printf(" %d", child_pids[i]); printf("\n"); } sigprocmask(SIG_SETMASK, &old_mask, NULL); } /* ---------- 命令行解析与执行 ---------- */ static void process_command(char *line) { /* 去掉末尾换行符 */ size_t len = strlen(line); if (len > 0 && line[len - 1] == '\n') line[len - 1] = '\0'; if (len == 0) return; /* 提取第一个 token */ char *cmd = strtok(line, " "); if (!cmd) return; if (strcmp(cmd, "create") == 0) { char *arg = strtok(NULL, " "); if (!arg) { printf("用法: create <进程数>\n"); return; } int n = atoi(arg); if (n <= 0) { printf("进程数必须为正整数\n"); return; } cmd_create(n); } else if (strcmp(cmd, "kill") == 0) { pid_t pids[256]; int n = 0; char *arg; while ((arg = strtok(NULL, " ")) && n < 256) pids[n++] = atoi(arg); if (n == 0) { printf("用法: kill ...\n"); return; } cmd_kill(n, pids); } else if (strcmp(cmd, "ps") == 0) { char *arg = strtok(NULL, " "); if (!arg || strcmp(arg, "-u") != 0) { printf("用法: ps -u\n"); return; } cmd_ps(); } else if (strcmp(cmd, "exit") == 0) { /* 检查是否还有子进程存活 */ sigset_t block_mask, old_mask; sigemptyset(&block_mask); sigaddset(&block_mask, SIGCHLD); sigprocmask(SIG_BLOCK, &block_mask, &old_mask); int remaining = child_count; sigprocmask(SIG_SETMASK, &old_mask, NULL); if (remaining > 0) { printf("还有 %d 个子进程未终止, 请先 kill 或等待其结束\n", remaining); return; } printf("所有子进程已结束, 父进程退出\n"); exit(0); } else { printf("未知命令: %s\n", cmd); printf("支持: create | kill ... | ps -u | exit\n"); } } int main(void) { /* 安装 SIGCHLD 处理程序 */ struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = sigchld_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_NOCLDSTOP | SA_RESTART; /* 仅在子进程终止时通知, 自动重启慢速系统调用 */ sigaction(SIGCHLD, &sa, NULL); /* 忽略 SIGTERM, 父进程不会被意外终止 */ signal(SIGTERM, SIG_IGN); printf("=== 子进程管理程序 ===\n"); printf("命令: create | kill ... | ps -u | exit\n"); char line[MAX_LINE]; while (1) { printf("> "); fflush(stdout); if (!fgets(line, sizeof(line), stdin)) break; /* EOF */ process_command(line); } printf("\n父进程退出\n"); return 0; }