添加以前的作业
This commit is contained in:
21
exp0.5/.claude/settings.local.json
Normal file
21
exp0.5/.claude/settings.local.json
Normal file
@@ -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/\")"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
28
exp0.5/.vscode/tasks.json
vendored
Normal file
28
exp0.5/.vscode/tasks.json
vendored
Normal file
@@ -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"
|
||||||
|
}
|
||||||
BIN
exp0.5/Linux进程控制编程(2).docx
Normal file
BIN
exp0.5/Linux进程控制编程(2).docx
Normal file
Binary file not shown.
54
exp0.5/task51.c
Normal file
54
exp0.5/task51.c
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
259
exp0.5/task52.c
Normal file
259
exp0.5/task52.c
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
294
exp0.5/task52_readable.c
Normal file
294
exp0.5/task52_readable.c
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
197
exp0.5/task53.c
Normal file
197
exp0.5/task53.c
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
205
exp0.5/task53_ai.c
Normal file
205
exp0.5/task53_ai.c
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
/*
|
||||||
|
* task53.c — 关键文件内容监控守护进程
|
||||||
|
*
|
||||||
|
* 功能: 每隔5分钟读取自身文件内容并计算hash值(数字指纹),
|
||||||
|
* 若hash值发生变化(文件被篡改), 则将检测时间和新hash值
|
||||||
|
* 写入日志文件 log。
|
||||||
|
*
|
||||||
|
* 用法:
|
||||||
|
* ./task53 以守护进程方式后台运行(每5分钟检查一次)
|
||||||
|
* ./task53 -f <秒> 前台运行, 可自定义检查间隔(便于测试)
|
||||||
|
* ./task53 test 自检模式: 验证hash算法正确性
|
||||||
|
*
|
||||||
|
* hash算法: 将文件按4字节切分, 每段视为一个无符号整数(小端序),
|
||||||
|
* 累加各段得到hash值。不足4字节的剩余部分按单字节累加。
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
243
exp0.5/task54.c
Normal file
243
exp0.5/task54.c
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
#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
|
||||||
|
|
||||||
|
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>\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 <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)
|
||||||
|
{
|
||||||
|
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 <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");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
257
exp0.5/task54_ai.c
Normal file
257
exp0.5/task54_ai.c
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user