258 lines
7.6 KiB
C
258 lines
7.6 KiB
C
|
|
/*
|
||
|
|
* task54.c — 子进程管理程序
|
||
|
|
*
|
||
|
|
* 借鉴 sigmask.c 的信号掩码方法管理子进程:
|
||
|
|
* 1. 在操作子进程列表时阻塞 SIGCHLD, 防止信号处理程序并发修改
|
||
|
|
* 2. 通过 sigwait / signal handler 等待子进程终止
|
||
|
|
*
|
||
|
|
* 支持命令:
|
||
|
|
* create <n> 创建 n 个子进程, 显示各子进程 PID
|
||
|
|
* kill <P1> ... 终止指定 PID 的子进程, 子进程打印 "killed by parent" 后退出
|
||
|
|
* ps -u 显示当前存活子进程列表
|
||
|
|
* exit 等待所有子进程结束后退出父进程
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <string.h>
|
||
|
|
#include <unistd.h>
|
||
|
|
#include <signal.h>
|
||
|
|
#include <sys/wait.h>
|
||
|
|
#include <errno.h>
|
||
|
|
|
||
|
|
#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 <P1> <P2> ...\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 <n> | kill <P1> ... | 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 <n> | kill <P1> <P2> ... | 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;
|
||
|
|
}
|