From 56a42330987f0c791642f9907663b729e7b55929 Mon Sep 17 00:00:00 2001 From: ChoChoX Date: Fri, 15 May 2026 21:25:01 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BB=A5=E5=89=8D=E7=9A=84?= =?UTF-8?q?=E4=BD=9C=E4=B8=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- exp0.5/.claude/settings.local.json | 21 +++ exp0.5/.vscode/tasks.json | 28 +++ exp0.5/Linux进程控制编程(2).docx | Bin 0 -> 49396 bytes exp0.5/task51.c | 54 ++++++ exp0.5/task52.c | 259 +++++++++++++++++++++++++ exp0.5/task52_readable.c | 294 +++++++++++++++++++++++++++++ exp0.5/task53.c | 197 +++++++++++++++++++ exp0.5/task53_ai.c | 205 ++++++++++++++++++++ exp0.5/task54.c | 243 ++++++++++++++++++++++++ exp0.5/task54_ai.c | 257 +++++++++++++++++++++++++ 10 files changed, 1558 insertions(+) create mode 100644 exp0.5/.claude/settings.local.json create mode 100644 exp0.5/.vscode/tasks.json create mode 100644 exp0.5/Linux进程控制编程(2).docx create mode 100644 exp0.5/task51.c create mode 100644 exp0.5/task52.c create mode 100644 exp0.5/task52_readable.c create mode 100644 exp0.5/task53.c create mode 100644 exp0.5/task53_ai.c create mode 100644 exp0.5/task54.c create mode 100644 exp0.5/task54_ai.c diff --git a/exp0.5/.claude/settings.local.json b/exp0.5/.claude/settings.local.json new file mode 100644 index 0000000..d7aef5e --- /dev/null +++ b/exp0.5/.claude/settings.local.json @@ -0,0 +1,21 @@ +{ + "permissions": { + "allow": [ + "Bash(gcc -Wall -o task53 task53.c)", + "Bash(./task53 test *)", + "Bash(gcc -Wall -o task54 task54.c)", + "Bash(timeout 10 bash -c ' *)", + "Bash(./task54)", + "Bash(timeout 15 bash /tmp/test_task54.sh)", + "Bash(python3 *)", + "Bash(pkill -9 -f \"task54\")", + "Bash(kill -9 38680 38681 38682 38944 38946 39691 39693 39694 39986 39987 40628)", + "Bash(pandoc \"/home/cho/C/exp2/Linux进程控制编程\\(2\\).docx\" -o /tmp/doc_output.md)", + "Bash(mkdir -p \"/home/cho/吕锦中202441429012405\")", + "Bash(cp /home/cho/C/exp2/task51.c \"/home/cho/吕锦中202441429012405/\")", + "Bash(cp /home/cho/C/exp2/task52.c \"/home/cho/吕锦中202441429012405/\")", + "Bash(cp /home/cho/C/exp2/task53.c \"/home/cho/吕锦中202441429012405/\")", + "Bash(cp /home/cho/C/exp2/task54.c \"/home/cho/吕锦中202441429012405/\")" + ] + } +} diff --git a/exp0.5/.vscode/tasks.json b/exp0.5/.vscode/tasks.json new file mode 100644 index 0000000..5d4653d --- /dev/null +++ b/exp0.5/.vscode/tasks.json @@ -0,0 +1,28 @@ +{ + "tasks": [ + { + "type": "cppbuild", + "label": "C/C++: gcc 生成活动文件", + "command": "/usr/bin/gcc", + "args": [ + "-fdiagnostics-color=always", + "-g", + "${file}", + "-o", + "${fileDirname}/${fileBasenameNoExtension}" + ], + "options": { + "cwd": "${fileDirname}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "detail": "调试器生成的任务。" + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git a/exp0.5/Linux进程控制编程(2).docx b/exp0.5/Linux进程控制编程(2).docx new file mode 100644 index 0000000000000000000000000000000000000000..31425d1b56aade5ef917a8ca4e6fdbc656fd3033 GIT binary patch literal 49396 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 literal 0 HcmV?d00001 diff --git a/exp0.5/task51.c b/exp0.5/task51.c new file mode 100644 index 0000000..dc877e6 --- /dev/null +++ b/exp0.5/task51.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include + +int main(){ + __pid_t pid; + + //父进程输出 + + printf("P1:I am father process,PID=%d,PPID=%d\n", getpid(), getppid()); + + //生成子进程 + pid = fork(); + + //父进程 + if(pid != 0){ + //pid判断归零 + pid = 0; + pid = fork(); + + //子进程 + if (pid == 0){ + printf("P12:I am young brother process,PID=%d,PPID=%d\n", getpid(), getppid()); + + //此时pid为0,继续生成子进程 + pid = fork(); + if (pid == 0){ + printf("P121:我的学号是2024414290124,PID=%d,PPID=%d\n", getpid(), getppid()); + } + //父进程 + else{ + //pid判断归零 + pid = 0; + pid = fork(); + //子进程 + if (pid == 0){ + printf("P122:我的姓名是吕锦中,PID=%d,PPID=%d\n", getpid(), getppid()); + } + //父进程 + else{ + } + } + } + //父进程 + else{ + } + } + //子进程 + else{ + time_t t = time(NULL); + printf("P11:当前时间是:%s,PID=%d,PPID=%d\n", ctime(&t), getpid(), getppid()); + } +} \ No newline at end of file diff --git a/exp0.5/task52.c b/exp0.5/task52.c new file mode 100644 index 0000000..9efd9fd --- /dev/null +++ b/exp0.5/task52.c @@ -0,0 +1,259 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_CMD 1024 +#define MAX_ARGS 64 +#define MAX_PIPES 32 + +char *trim(char *s) { + while (isspace((unsigned char)*s)) s++; + if (*s == '\0') return s; + char *end = s + strlen(s) - 1; + while (end > s && isspace((unsigned char)*end)) end--; + *(end + 1) = '\0'; + return s; +} + +int parse_args(char *cmd, char **args) { + int argc = 0; + char *token = strtok(cmd, " \t"); + while (token != NULL && argc < MAX_ARGS - 1) { + args[argc++] = token; + token = strtok(NULL, " \t"); + } + args[argc] = NULL; + return argc; +} + +int split_pipeline(char *buf, char *segments[], int max_segs) { + int nseg = 0; + char *seg_start = buf; /* 当前段的起始位置 */ + char *p = buf; + + while (*p != '\0') { + if (*p == '|') { + /* 找到管道符:截断当前段,保存去空格后的指针 */ + *p = '\0'; + char *seg = trim(seg_start); + if (seg[0] != '\0') { /* 跳过空段 */ + if (nseg >= max_segs) { + fprintf(stderr, "shell: too many pipes (max %d)\n", max_segs); + return -1; + } + segments[nseg++] = seg; + } + seg_start = p + 1; /* 下一段从 '|' 后面开始 */ + } + p++; + } + + /* 保存最后一段('|' 后面,或整个命令没有 '|' 时) */ + char *last = trim(seg_start); + if (last[0] != '\0') { + segments[nseg++] = last; + } + + return nseg; +} + +static void exec_cmd_in_child(char **args, int argc, int in_fd, int out_fd) { + char *infile = NULL; + char *outfile = NULL; + char *clean_args[MAX_ARGS]; + int clean_argc = 0; + + for (int i = 0; i < argc; i++) { + if (strcmp(args[i], "<") == 0 && i + 1 < argc) { + infile = args[++i]; /* 记录输入文件,跳过文件名 */ + } else if (strcmp(args[i], ">") == 0 && i + 1 < argc) { + outfile = args[++i]; /* 记录输出文件,跳过文件名 */ + } else { + clean_args[clean_argc++] = args[i]; /* 普通参数保留 */ + } + } + clean_args[clean_argc] = NULL; + + if (clean_argc == 0) + { + exit(0); + } + + if (infile) { + if (in_fd != STDIN_FILENO) + close(in_fd); /* 管道读端不再需要,关掉 */ + + int fd = open(infile, O_RDONLY); + + if (fd < 0) { + perror("open"); exit(1); + } + dup2(fd, STDIN_FILENO); + close(fd); + } else if (in_fd != STDIN_FILENO) { + dup2(in_fd, STDIN_FILENO); + close(in_fd); + } + + if (outfile) { + if (out_fd != STDOUT_FILENO) + close(out_fd); + int fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) { + perror("open"); exit(1); + } + dup2(fd, STDOUT_FILENO); + close(fd); + } else if (out_fd != STDOUT_FILENO) { + dup2(out_fd, STDOUT_FILENO); + close(out_fd); + } + + execvp(clean_args[0], clean_args); + fprintf(stderr, "shell: command not found: %s\n", clean_args[0]); + exit(127); +} + +static void run_single(char *seg) { + char buf[MAX_CMD]; + strncpy(buf, seg, MAX_CMD - 1); + buf[MAX_CMD - 1] = '\0'; + + char *args[MAX_ARGS]; + int argc = parse_args(buf, args); + if (argc == 0) return; + + pid_t pid = fork(); + if (pid < 0) { perror("fork"); return; } + if (pid == 0) { + /* 子进程:从终端 stdin 读,输出到终端 stdout */ + exec_cmd_in_child(args, argc, STDIN_FILENO, STDOUT_FILENO); + } + /* 父进程:等待子进程结束 */ + waitpid(pid, NULL, 0); +} + +static void run_pipeline(char *segments[], int nseg) { + int npipes = nseg - 1; + + /* --- 1. 一次性创建所有管道 --- */ + int pipes[MAX_PIPES][2]; + for (int i = 0; i < npipes; i++) { + if (pipe(pipes[i]) < 0) { + perror("pipe"); + /* 出错:关闭已创建的管道后返回 */ + for (int j = 0; j < i; j++) { + close(pipes[j][0]); + close(pipes[j][1]); + } + return; + } + } + + /* --- 2. 依次 fork 每段命令的子进程 --- */ + pid_t pids[MAX_PIPES + 1]; + + for (int i = 0; i < nseg; i++) { + + /* 确定本段从哪里读、往哪里写: + * 第一段:从终端 stdin 读 + * 中间段:从上一个管道的读端读,向下一个管道的写端写 + * 最后段:向终端 stdout 写 */ + int in_fd = (i == 0) ? STDIN_FILENO : pipes[i - 1][0]; + int out_fd = (i == nseg - 1) ? STDOUT_FILENO : pipes[i][1]; + + /* 解析本段命令的参数 */ + char cbuf[MAX_CMD]; + strncpy(cbuf, segments[i], MAX_CMD - 1); + cbuf[MAX_CMD - 1] = '\0'; + char *args[MAX_ARGS]; + int argc = parse_args(cbuf, args); + + pid_t pid = fork(); + if (pid < 0) { + perror("fork"); + /* fork 失败:关闭所有管道,等待已启动的子进程 */ + for (int j = 0; j < npipes; j++) { + close(pipes[j][0]); + close(pipes[j][1]); + } + for (int j = 0; j < i; j++) waitpid(pids[j], NULL, 0); + return; + } + + if (pid == 0) { + /* 子进程: + * 关闭所有"不是自己用"的管道端, + * 防止 EOF 永远不到达读端。 */ + for (int j = 0; j < npipes; j++) { + if (pipes[j][0] != in_fd) + close(pipes[j][0]); + if (pipes[j][1] != out_fd) + close(pipes[j][1]); + } + if (argc == 0) + exit(0); + exec_cmd_in_child(args, argc, in_fd, out_fd); + /* exec_cmd_in_child 内部会 exit,永远不会走到这里 */ + } + + pids[i] = pid; /* 父进程记录子进程 PID */ + } + + /* --- 3. 父进程关闭所有管道端,再统一等待子进程 --- + * 必须先全部关闭,否则最后一段命令永远读不到 EOF。 */ + for (int i = 0; i < npipes; i++) { + close(pipes[i][0]); + close(pipes[i][1]); + } + for (int i = 0; i < nseg; i++) { + waitpid(pids[i], NULL, 0); + } +} + +void execute(char *cmdline) { + char buf[MAX_CMD]; + strncpy(buf, cmdline, MAX_CMD - 1); + buf[MAX_CMD - 1] = '\0'; + + char *segments[MAX_PIPES + 1]; + int nseg = split_pipeline(buf, segments, MAX_PIPES); + + if (nseg <= 0) return; /* 空命令或出错 */ + + if (nseg == 1) { + run_single(segments[0]); /* 普通命令,无管道 */ + } else { + run_pipeline(segments, nseg); /* 多段管道命令 */ + } +} + +int main() { + char cmdline[MAX_CMD]; + + while (1) { + printf("%% "); + fflush(stdout); + + if (fgets(cmdline, MAX_CMD, stdin) == NULL) { + printf("\n"); + break; + } + + char *cmd = trim(cmdline); + if (strlen(cmd) == 0) continue; + + if (strcmp(cmd, "exit") == 0 || strcmp(cmd, "logout") == 0) { + printf("Goodbye!\n"); + break; + } + + execute(cmd); + } + return 0; +} \ No newline at end of file diff --git a/exp0.5/task52_readable.c b/exp0.5/task52_readable.c new file mode 100644 index 0000000..bbeca55 --- /dev/null +++ b/exp0.5/task52_readable.c @@ -0,0 +1,294 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_CMD 1024 +#define MAX_ARGS 64 +#define MAX_PIPES 32 + +/* ========================================================= + * 工具函数 + * ========================================================= */ + +/* 去除字符串首尾空白字符(在原字符串上修改) */ +char *trim(char *s) { + while (isspace((unsigned char)*s)) s++; + if (*s == '\0') return s; + char *end = s + strlen(s) - 1; + while (end > s && isspace((unsigned char)*end)) end--; + *(end + 1) = '\0'; + return s; +} + +/* 把命令字符串按空格拆成 args[],返回参数个数 */ +int parse_args(char *cmd, char **args) { + int argc = 0; + char *token = strtok(cmd, " \t"); + while (token != NULL && argc < MAX_ARGS - 1) { + args[argc++] = token; + token = strtok(NULL, " \t"); + } + args[argc] = NULL; + return argc; +} + +/* ========================================================= + * 第一步辅助函数:split_pipeline + * 把 "cmd1 | cmd2 | cmd3" 按 '|' 拆成若干段, + * 存入 segments[],返回段数。 + * 注意:会修改 buf 的内容(把 '|' 替换为 '\0')。 + * ========================================================= */ +int split_pipeline(char *buf, char *segments[], int max_segs) { + int nseg = 0; + char *seg_start = buf; /* 当前段的起始位置 */ + char *p = buf; + + while (*p != '\0') { + if (*p == '|') { + /* 找到管道符:截断当前段,保存去空格后的指针 */ + *p = '\0'; + char *seg = trim(seg_start); + if (seg[0] != '\0') { /* 跳过空段 */ + if (nseg >= max_segs) { + fprintf(stderr, "shell: too many pipes (max %d)\n", max_segs); + return -1; + } + segments[nseg++] = seg; + } + seg_start = p + 1; /* 下一段从 '|' 后面开始 */ + } + p++; + } + + /* 保存最后一段('|' 后面,或整个命令没有 '|' 时) */ + char *last = trim(seg_start); + if (last[0] != '\0') { + segments[nseg++] = last; + } + + return nseg; +} + +/* ========================================================= + * 第二步辅助函数:exec_cmd_in_child + * 在子进程里执行一条命令,处理 < 和 > 重定向。 + * in_fd / out_fd 来自管道(或 STDIN/STDOUT_FILENO)。 + * 此函数只能在 fork() 后的子进程里调用,内部会 exit()。 + * ========================================================= */ +static void exec_cmd_in_child(char **args, int argc, int in_fd, int out_fd) { + /* --- 解析重定向符,把 < file 和 > file 从参数里剥离 --- */ + char *infile = NULL; + char *outfile = NULL; + char *clean_args[MAX_ARGS]; + int clean_argc = 0; + + for (int i = 0; i < argc; i++) { + if (strcmp(args[i], "<") == 0 && i + 1 < argc) { + infile = args[++i]; /* 记录输入文件,跳过文件名 */ + } else if (strcmp(args[i], ">") == 0 && i + 1 < argc) { + outfile = args[++i]; /* 记录输出文件,跳过文件名 */ + } else { + clean_args[clean_argc++] = args[i]; /* 普通参数保留 */ + } + } + clean_args[clean_argc] = NULL; + + if (clean_argc == 0) exit(0); + + /* --- 设置标准输入 --- + * 优先级:< 文件重定向 > 管道读端 > 默认终端 stdin */ + if (infile) { + if (in_fd != STDIN_FILENO) close(in_fd); /* 管道读端不再需要,关掉 */ + int fd = open(infile, O_RDONLY); + if (fd < 0) { perror("open"); exit(1); } + dup2(fd, STDIN_FILENO); + close(fd); + } else if (in_fd != STDIN_FILENO) { + dup2(in_fd, STDIN_FILENO); + close(in_fd); + } + + /* --- 设置标准输出 --- + * 优先级:> 文件重定向 > 管道写端 > 默认终端 stdout */ + if (outfile) { + if (out_fd != STDOUT_FILENO) close(out_fd); + int fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) { perror("open"); exit(1); } + dup2(fd, STDOUT_FILENO); + close(fd); + } else if (out_fd != STDOUT_FILENO) { + dup2(out_fd, STDOUT_FILENO); + close(out_fd); + } + + execvp(clean_args[0], clean_args); + fprintf(stderr, "shell: command not found: %s\n", clean_args[0]); + exit(127); +} + +/* ========================================================= + * 第三步辅助函数:run_single + * 执行单条命令(无管道),fork 一个子进程并等待它结束。 + * ========================================================= */ +static void run_single(char *seg) { + char buf[MAX_CMD]; + strncpy(buf, seg, MAX_CMD - 1); + buf[MAX_CMD - 1] = '\0'; + + char *args[MAX_ARGS]; + int argc = parse_args(buf, args); + if (argc == 0) return; + + pid_t pid = fork(); + if (pid < 0) { perror("fork"); return; } + if (pid == 0) { + /* 子进程:从终端 stdin 读,输出到终端 stdout */ + exec_cmd_in_child(args, argc, STDIN_FILENO, STDOUT_FILENO); + } + /* 父进程:等待子进程结束 */ + waitpid(pid, NULL, 0); +} + +/* ========================================================= + * 第四步辅助函数:run_pipeline + * 执行多段管道命令。 + * + * 核心思路(以 "A | B | C" 为例): + * + * 终端stdin ──> [A] ──pipe0──> [B] ──pipe1──> [C] ──> 终端stdout + * + * 1. 创建 nseg-1 个管道(pipe0, pipe1, ...) + * 2. 依次 fork 每段命令的子进程,子进程拿到自己的 in_fd/out_fd + * 3. 父进程关闭所有管道端,然后等待全部子进程结束 + * ========================================================= */ +static void run_pipeline(char *segments[], int nseg) { + int npipes = nseg - 1; + + /* --- 1. 一次性创建所有管道 --- */ + int pipes[MAX_PIPES][2]; + for (int i = 0; i < npipes; i++) { + if (pipe(pipes[i]) < 0) { + perror("pipe"); + /* 出错:关闭已创建的管道后返回 */ + for (int j = 0; j < i; j++) { + close(pipes[j][0]); + close(pipes[j][1]); + } + return; + } + } + + /* --- 2. 依次 fork 每段命令的子进程 --- */ + pid_t pids[MAX_PIPES + 1]; + + for (int i = 0; i < nseg; i++) { + + /* 确定本段从哪里读、往哪里写: + * 第一段:从终端 stdin 读 + * 中间段:从上一个管道的读端读,向下一个管道的写端写 + * 最后段:向终端 stdout 写 */ + int in_fd = (i == 0) ? STDIN_FILENO : pipes[i - 1][0]; + int out_fd = (i == nseg - 1) ? STDOUT_FILENO : pipes[i][1]; + + /* 解析本段命令的参数 */ + char cbuf[MAX_CMD]; + strncpy(cbuf, segments[i], MAX_CMD - 1); + cbuf[MAX_CMD - 1] = '\0'; + char *args[MAX_ARGS]; + int argc = parse_args(cbuf, args); + + pid_t pid = fork(); + if (pid < 0) { + perror("fork"); + /* fork 失败:关闭所有管道,等待已启动的子进程 */ + for (int j = 0; j < npipes; j++) { + close(pipes[j][0]); + close(pipes[j][1]); + } + for (int j = 0; j < i; j++) waitpid(pids[j], NULL, 0); + return; + } + + if (pid == 0) { + /* 子进程: + * 关闭所有"不是自己用"的管道端, + * 防止 EOF 永远不到达读端。 */ + for (int j = 0; j < npipes; j++) { + if (pipes[j][0] != in_fd) close(pipes[j][0]); + if (pipes[j][1] != out_fd) close(pipes[j][1]); + } + if (argc == 0) exit(0); + exec_cmd_in_child(args, argc, in_fd, out_fd); + /* exec_cmd_in_child 内部会 exit,永远不会走到这里 */ + } + + pids[i] = pid; /* 父进程记录子进程 PID */ + } + + /* --- 3. 父进程关闭所有管道端,再统一等待子进程 --- + * 必须先全部关闭,否则最后一段命令永远读不到 EOF。 */ + for (int i = 0; i < npipes; i++) { + close(pipes[i][0]); + close(pipes[i][1]); + } + for (int i = 0; i < nseg; i++) { + waitpid(pids[i], NULL, 0); + } +} + +/* ========================================================= + * 顶层入口:execute + * 只做"分流": + * - 把命令行按 '|' 分段 + * - 1 段 → run_single + * - 多段 → run_pipeline + * ========================================================= */ +void execute(char *cmdline) { + char buf[MAX_CMD]; + strncpy(buf, cmdline, MAX_CMD - 1); + buf[MAX_CMD - 1] = '\0'; + + char *segments[MAX_PIPES + 1]; + int nseg = split_pipeline(buf, segments, MAX_PIPES); + + if (nseg <= 0) return; /* 空命令或出错 */ + + if (nseg == 1) { + run_single(segments[0]); /* 普通命令,无管道 */ + } else { + run_pipeline(segments, nseg); /* 多段管道命令 */ + } +} + +/* ========================================================= + * main:读取命令行,循环执行 + * ========================================================= */ +int main() { + char cmdline[MAX_CMD]; + + while (1) { + printf("%% "); + fflush(stdout); + + if (fgets(cmdline, MAX_CMD, stdin) == NULL) { + printf("\n"); + break; + } + + char *cmd = trim(cmdline); + if (strlen(cmd) == 0) continue; + + if (strcmp(cmd, "exit") == 0 || strcmp(cmd, "logout") == 0) { + printf("Goodbye!\n"); + break; + } + + execute(cmd); + } + return 0; +} diff --git a/exp0.5/task53.c b/exp0.5/task53.c new file mode 100644 index 0000000..c776cf8 --- /dev/null +++ b/exp0.5/task53.c @@ -0,0 +1,197 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define INTERVAL_SEC 10 +#define LOG_FILE "log" + +static unsigned int compute_hash(const char *data, size_t len) +{ + unsigned int hash =0; + const unsigned char *p = (const unsigned char *)data; + size_t i,n = len /4; + + for ( i = 0; i < n; i++) + { + unsigned int val = (unsigned int)p[i * 4] | ((unsigned int)p[i * 4 + 1] << 8) + | ((unsigned int)p[i * 4 + 2] << 16) | ((unsigned int)p[i * 4 + 3] << 24); + hash += val; + } + + size_t rem = len % 4,base = n*4; + for ( i = 0; i < rem; i++) + { + hash += (unsigned int)p[base + i]; + } + return hash; +} + + +static char *read_file(const char *path,size_t *out_len) +{ + int fd = open(path,O_RDONLY); + if (fd < 0) + { + return NULL; + } + + off_t size = lseek(fd,0,SEEK_END); + if (size < 0) + { + close(fd); + return NULL; + } + lseek(fd,0,SEEK_SET); + + char *buf = malloc((size_t)size + 1); + if (!buf) + { + close(fd); + return NULL; + } + + ssize_t n = read(fd,buf,(size_t)size); + if (n < 0) + { + free(buf); + close(fd); + return NULL; + } + + buf[n] = '\0'; + *out_len = (size_t)n; + close(fd); + return buf; +} + +static void write_log(unsigned int hash) +{ + FILE *fp = fopen(LOG_FILE,"a"); + if (!fp) + { + return; + } + + time_t now = time(NULL); + struct tm *tm_info = localtime(&now); + char time_str[20]; + strftime(time_str,sizeof(time_str),"%Y-%m-%d %H:%M:%S",tm_info); + + fprintf(fp,"%s hash=%u\n",time_str,hash); + fclose(fp); +} + +static void daemonize(void) +{ + pid_t pid = fork(); + if (pid < 0) + { + exit(1); + } + if (pid > 0) + { + exit(0); + } + + setsid(); + umask(0); + + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); +} + + +int main(int argc,char *argv[]) +{ + //自检模式 + if (argc > 1 && strcmp(argv[1],"test") == 0) + { + /* 测试1: 不同内容应产生不同hash值 */ + printf("自检模式\n"); + + const char *test_str = "Hello, World!"; + unsigned int h1 = compute_hash(test_str,strlen(test_str)); + + const char *test_str2 = "Hello, world!"; // 注意 'W' 和 'w' + unsigned int h2 = compute_hash(test_str2,strlen(test_str2)); + + printf("测试1: hash1=%u, hash2=%u\n",h1,h2); + if (h1 != h2) + printf("通过: 不同内容 -> 不同hash值\n"); + else + printf("失败: 不同内容应产生不同的hash值\n"); + + + /* 测试2: 相同内容应产生相同hash值 */ + unsigned int h3 = compute_hash(test_str,strlen(test_str)); + printf("测试2: hash3=%u\n",h3); + if (h1 == h3) + printf("通过: 相同内容 -> 相同hash值\n"); + else + printf("失败: 相同内容应产生相同的hash值\n"); + + /* 测试3: 读取自身并计算hash */ + size_t len; + char *self = read_file("task53.c",&len); + if (self) + { + unsigned int hself = compute_hash(self,len); + printf("测试3: 自身hash=%u, 文件大小=%zu字节\n",hself,len); + free(self); + } + return 0; + } + + /* + * 守护进程模式 + * -f 表示前台运行(不调用daemonize), 便于测试 + * 第二个参数可指定检查间隔秒数 + */ + int foreground = (argc > 1 && strcmp(argv[1],"-f") == 0); + int interval = INTERVAL_SEC; + if (argc > 2) interval = atoi(argv[2]); + if (interval <= 0) interval = INTERVAL_SEC; + + if (!foreground) + { + daemonize(); + } + + size_t len; + char *prev_data = read_file("test.log",&len); + if (!prev_data) return 1; + unsigned int prev_hash = compute_hash(prev_data,len); + + while (1) + { + sleep(interval); + + size_t cur_len; + char *cur_data = read_file("test.log",&cur_len); + if (!cur_data) + { + continue; + } + + unsigned int cur_hash = compute_hash(cur_data,cur_len); + if (cur_hash != prev_hash) + { + write_log(cur_hash); + free(prev_data); + prev_data = cur_data; + prev_hash = cur_hash; + } + else + { + free(cur_data); + } + } + free(prev_data); + return 0; +} diff --git a/exp0.5/task53_ai.c b/exp0.5/task53_ai.c new file mode 100644 index 0000000..c6dfe53 --- /dev/null +++ b/exp0.5/task53_ai.c @@ -0,0 +1,205 @@ +/* + * task53.c — 关键文件内容监控守护进程 + * + * 功能: 每隔5分钟读取自身文件内容并计算hash值(数字指纹), + * 若hash值发生变化(文件被篡改), 则将检测时间和新hash值 + * 写入日志文件 log。 + * + * 用法: + * ./task53 以守护进程方式后台运行(每5分钟检查一次) + * ./task53 -f <秒> 前台运行, 可自定义检查间隔(便于测试) + * ./task53 test 自检模式: 验证hash算法正确性 + * + * hash算法: 将文件按4字节切分, 每段视为一个无符号整数(小端序), + * 累加各段得到hash值。不足4字节的剩余部分按单字节累加。 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define INTERVAL_SEC (5 * 60) /* 默认检查间隔: 5分钟 */ +#define LOG_FILE "log" /* 日志文件名 */ + +/* + * 计算数据的hash值(数字指纹) + * data: 文件内容指针, len: 数据长度(字节) + * 返回: 32位无符号hash值 + */ +static unsigned int compute_hash(const char *data, size_t len) +{ + unsigned int hash = 0; + const unsigned char *p = (const unsigned char *)data; + size_t i, n = len / 4; /* 完整的4字节段个数 */ + + /* 每4个字节拼成一个无符号整数(小端序: 低地址为低字节) */ + for (i = 0; i < n; i++) { + unsigned int val = (unsigned int)p[i * 4] + | ((unsigned int)p[i * 4 + 1] << 8) + | ((unsigned int)p[i * 4 + 2] << 16) + | ((unsigned int)p[i * 4 + 3] << 24); + hash += val; /* 累加到hash值 */ + } + + /* 处理末尾不足4字节的剩余部分, 逐字节累加 */ + size_t rem = len % 4, base = n * 4; + for (i = 0; i < rem; i++) + hash += (unsigned int)p[base + i]; + + return hash; +} + +/* + * 读取整个文件到内存 + * path: 文件路径, out_len: 输出参数, 返回文件长度 + * 返回: 动态分配的缓冲区指针(调用者负责free), 失败返回NULL + */ +static char *read_file(const char *path, size_t *out_len) +{ + int fd = open(path, O_RDONLY); + if (fd < 0) return NULL; /* 文件打开失败 */ + + /* 通过lseek获取文件大小 */ + off_t size = lseek(fd, 0, SEEK_END); + if (size < 0) { close(fd); return NULL; } + lseek(fd, 0, SEEK_SET); /* 回到文件开头 */ + + char *buf = malloc((size_t)size + 1); /* 多分配1字节放'\0' */ + if (!buf) { close(fd); return NULL; } + + ssize_t n = read(fd, buf, (size_t)size); + if (n < 0) { free(buf); close(fd); return NULL; } + + buf[n] = '\0'; /* 字符串结尾 */ + *out_len = (size_t)n; + close(fd); + return buf; +} + +/* + * 将篡改检测记录写入日志文件 + * hash: 检测到的新hash值 + * 日志格式: YYYY-MM-DD HH:MM:SS hash=<值> + */ +static void write_log(unsigned int hash) +{ + FILE *fp = fopen(LOG_FILE, "a"); /* 追加模式打开日志 */ + if (!fp) return; + + time_t now = time(NULL); /* 获取当前时间 */ + char ts[64]; + strftime(ts, sizeof(ts), "%Y-%m-%d %H:%M:%S", localtime(&now)); + + fprintf(fp, "%s hash=%u\n", ts, hash); /* 写入时间和hash */ + fclose(fp); +} + +/* + * 将进程转为守护进程(daemon) + * 步骤: fork子进程并退出父进程 -> 创建新会话 -> 关闭标准IO + */ +static void daemonize(void) +{ + pid_t pid = fork(); + if (pid < 0) exit(1); /* fork失败 */ + if (pid > 0) exit(0); /* 父进程退出, 子进程继续 */ + + setsid(); /* 创建新会话, 脱离控制终端 */ + umask(0); /* 重置文件权限掩码 */ + + /* 关闭标准输入、输出、错误, 守护进程不需要终端IO */ + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); +} + +int main(int argc, char *argv[]) +{ + /* + * 自检模式: ./task53 test + * 验证hash算法 — 不同内容产生不同hash, 相同内容产生相同hash + */ + if (argc > 1 && strcmp(argv[1], "test") == 0) { + printf("=== 自检模式 ===\n"); + + /* 测试1: 不同内容应产生不同hash值 */ + const char *data1 = "hello world"; + unsigned int h1 = compute_hash(data1, strlen(data1)); + printf("hash(\"%s\") = %u\n", data1, h1); + + const char *data2 = "hello worlD"; /* 仅一个字节不同 */ + unsigned int h2 = compute_hash(data2, strlen(data2)); + printf("hash(\"%s\") = %u\n", data2, h2); + + if (h1 != h2) + printf("通过: 不同内容 -> 不同hash值\n"); + else + printf("失败: 不同内容应产生不同的hash值\n"); + + /* 测试2: 相同内容应产生相同hash值 */ + unsigned int h3 = compute_hash(data1, strlen(data1)); + if (h1 == h3) + printf("通过: 相同内容 -> 相同hash值\n"); + else + printf("失败: 相同内容应产生相同的hash值\n"); + + /* 测试3: 读取自身并计算hash */ + size_t len; + char *self = read_file("task53.c", &len); + if (self) { + unsigned int hself = compute_hash(self, len); + printf("自身hash = %u, 文件大小 = %zu 字节\n", hself, len); + free(self); + } + return 0; + } + + /* + * 守护进程模式 + * -f 表示前台运行(不调用daemonize), 便于测试 + * 第二个参数可指定检查间隔秒数 + */ + int foreground = (argc > 1 && strcmp(argv[1], "-f") == 0); + int interval = INTERVAL_SEC; + if (argc > 2) interval = atoi(argv[2]); /* 自定义检查间隔 */ + if (interval <= 0) interval = INTERVAL_SEC; + + if (!foreground) daemonize(); /* 后台模式则转为守护进程 */ + + /* 读取文件初始快照, 计算基准hash值 */ + size_t len; + char *prev_data = read_file("task53.c", &len); + if (!prev_data) return 1; /* 读取失败则退出 */ + unsigned int prev_hash = compute_hash(prev_data, len); + + /* 主循环: 每隔interval秒检查一次 */ + while (1) { + sleep(interval); + + /* 重新读取文件内容 */ + size_t cur_len; + char *cur_data = read_file("task53.c", &cur_len); + if (!cur_data) continue; /* 读取失败则跳过本次 */ + + /* 计算当前hash并与基准hash比较 */ + unsigned int cur_hash = compute_hash(cur_data, cur_len); + if (cur_hash != prev_hash) { + /* 检测到篡改: 写入日志 */ + write_log(cur_hash); + /* 更新基准快照, 避免同一修改被重复记录 */ + free(prev_data); + prev_data = cur_data; + prev_hash = cur_hash; + } else { + free(cur_data); /* 未变化则释放当前数据 */ + } + } + + free(prev_data); + return 0; +} diff --git a/exp0.5/task54.c b/exp0.5/task54.c new file mode 100644 index 0000000..f3c0a43 --- /dev/null +++ b/exp0.5/task54.c @@ -0,0 +1,243 @@ +#include +#include +#include +#include +#include +#include +#include + +#define MAX_CHILDREN 1024 +#define MAX_LINE 256 + +static pid_t child_pids[MAX_CHILDREN]; +static int child_count = 0; + +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"); + + 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) +{ + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sigemptyset(&sa.sa_mask); + + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGTERM); + sigprocmask(SIG_BLOCK, &mask, NULL); + + printf("子进程 %d 启动\n", getpid()); + + /* 等待 SIGTERM 信号 */ + int sig; + sigwait(&mask, &sig); + + printf("killed by parent\n"); + exit(100); +} + +static void cmd_create(int n) +{ + sigset_t block_mask, old_mask; + + 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_pids[child_count++] = pid; + created++; + } + + sigprocmask(SIG_SETMASK, &old_mask, NULL); + + if (created > 0) { + printf("已创建 %d 个子进程:", created); + 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++) { + 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; + } + if (kill(pids[i], SIGTERM) == 0) { + printf("已向子进程 %d 发送终止信号\n", pids[i]); + } else { + perror("kill"); + } + } +} + +static void cmd_ps(void) +{ + sigset_t block_mask, old_mask; + + 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; + } + + 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("无效的子进程数量: %s\n", arg); + 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) +{ + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sigchld_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; + 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"); + return 0; +} \ No newline at end of file diff --git a/exp0.5/task54_ai.c b/exp0.5/task54_ai.c new file mode 100644 index 0000000..4561f36 --- /dev/null +++ b/exp0.5/task54_ai.c @@ -0,0 +1,257 @@ +/* + * 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; +}