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 0000000..31425d1 Binary files /dev/null and b/exp0.5/Linux进程控制编程(2).docx differ 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; +}