添加以前的作业

This commit is contained in:
2026-05-15 21:25:01 +08:00
parent fa2f3e2413
commit 56a4233098
10 changed files with 1558 additions and 0 deletions

View 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
View 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"
}

Binary file not shown.

54
exp0.5/task51.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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;
}