1.实验目的
1.了解proc文件系统
2.理解task_struct结构及内核进程控制块链表遍历机制
3.采用添加内核模块技术和访问/proc目录方法,打印进程树
2.实验截图及结果分析
(1)实验截图
①访问/proc目录方法打印进程树
②添加内核模块技术打印进程树
(2)实验结果分析
从实验截图结果来看,通过访问/proc目录和添加内核模块这两种方式,均成功打印出了proc文件中包含的进程信息,且在进程树层级结构的呈现效果上各有特点。
用户态编程(访问/proc目录方法)通过编写的pstree.c代码,实现了从/proc下各进程status文件中读取、解析关键信息,my_getpid和my_getppid函数成功提取出进程的PID和PPID,特定的字符串处理逻辑解析出进程名,在print_pstree函数里,借助统计子进程数量和记录已打印子进程数,依据递归深度精准控制缩进和连接符的打印,最终呈现的进程树清晰展示了进程名、进程id和父进程id,层级结构一目了然,父子进程关系清晰可辨,极大方便了对进程间关系的理解和分析。
内核态编程(添加内核模块技术)中,pstree2.c的print_tree2函数借助list_for_each遍历cur->children链表,运用深度优先算法递归打印子进程,从实验截图的打印结果来看,不仅呈现了进程名,还包含了PID号,并且通过缩进和特定符号形成了树形结构,清晰体现了进程间的层级关系,让进程树的展示更加直观,既保留了内核态遍历的高效性,又通过补充PID和优化结构增强了信息的完整性和可读性。
3.实验程序
(1)访问/proc目录方法打印进程树
pstree.c
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <netdb.h> #include <sys/types.h> #include <pthread.h> #include <unistd.h> #include <dirent.h> char default_path[1024] = "/proc/"; int s = 0; typedef struct file_info { int pid; int ppid; char name[1024]; int flag; int rec; } info; // 从字符串中提取 PID int my_getpid(char *str) { int len = strlen(str); char num[10]; int i, j, ret; if (strncmp(str, "Pid", 3) == 0) { for (i = 0; i < len; i++) { if (str[i] >= '0' && str[i] <= '9') break; } for (j = 0; j < len - i; j++) { num[j] = str[i + j]; } ret = atoi(num); } else ret = 0; return ret; } // 从字符串中提取 PPID int my_getppid(char *str) { int len = strlen(str); char num[10]; int i, j, ret; if (strncmp(str, "PPid", 4) == 0) { for (i = 0; i < len; i++) { if (str[i] >= '0' && str[i] <= '9') break; } for (j = 0; j < len - i; j++) { num[j] = str[i + j]; } ret = atoi(num); } else ret = 0; return ret; } // 检查是否存在指定父进程 ID 的子进程 int child_exist(info *file, int count, int ppid) { int i; for (i = 0; i < count; i++) { if (file[i].flag == 0 && file[i].ppid == ppid) return 1; } return 0; } // 递归打印进程树 void print_pstree(info *file, int count, int ppid, int rec) { int i, j, k; int child_count = 0; // 记录当前父进程下子进程数量 int printed_child = 0; // 记录已打印子进程数量 // 统计子进程数量 for (i = 0; i < count; i++) { if (file[i].flag == 0 && file[i].ppid == ppid) { child_count++; } } for (i = 0; i < count; i++) { if (file[i].flag == 0 && file[i].ppid == ppid) { file[i].rec = rec + 1; file[i].flag = 1; // 打印缩进和连接符 for (k = 0; k < rec; k++) { if (k < rec - 1) { printf("│ "); } else { if (printed_child == child_count - 1) { printf(" "); } else { printf("├── "); } } } printf("[%d]%s\n", file[i].pid, file[i].name); print_pstree(file, count, file[i].pid, file[i].rec); printed_child++; } } } int main() { int i, j, k, total, s1, s2, count, t; char str[1024], dir[1024]; struct dirent **namelist; strcpy(dir, default_path); total = scandir(dir, &namelist, 0, alphasort); printf("path=%s,total=%d\n", dir, total); count = 0; for (i = 0; i < total; i++) { strcpy(str, namelist[i]->d_name); if (str[0] >= '0' && str[0] <= '9') count++; } printf("进程数:%d\n", count); info file[1024]; i = 0; t = 0; while (i < total) { FILE *fp; char path[1024], name[1024]; int pid, ppid; strcpy(str, namelist[i]->d_name); strcpy(path, default_path); if (str[0] >= '0' && str[0] <= '9') { strcat(path, str); strcat(path, "/status"); fp = fopen(path, "r"); while (!feof(fp)) { fgets(str, 1024, fp); if ((s1 = my_getpid(str)) != 0) pid = s1; if ((s2 = my_getppid(str)) != 0) ppid = s2; if (strncmp(str, "Name", 4) == 0) { for (j = 4; j < strlen(str); j++) { if (str[j] >= 'a' && str[j] <= 'z') break; } for (k = j; k < strlen(str); k++) { name[k - j] = str[k]; } name[k - j - 1] = '\0'; } file[t].pid = pid; file[t].ppid = ppid; strcpy(file[t].name, name); } fclose(fp); t++; } i++; } // 初始化标志位和递归深度 for (i = 0; i < count; i++) { file[i].flag = 0; file[i].rec = 0; } print_pstree(file, count, 0, 0); return 0; }(2)添加内核模块技术打印进程树
①pstree2.c
#include<linux/module.h> #include<linux/sched.h> #include<linux/list.h> typedef struct task_struct ts; // 递归打印进程树 void print_tree2(ts *cur,int blanks){ int i; for(i=1;i<=blanks;i++) printk(" "); printk("%s\n",cur->comm); struct list_head *list; list_for_each(list,&cur->children){ ts *p=list_entry(list,struct task_struct,sibling); print_tree2(p,blanks+4); } } // 模块初始化函数 int init_pstree2(void){ ts *cur; // 找到 init 进程(PID 为 1) for(cur = current; cur->pid != 1; cur = cur->parent); print_tree2(cur,0); return 0; } // 模块退出函数 void exit_pstree2(void){ } // 注册模块初始化函数 module_init(init_pstree2); // 注册模块退出函数 module_exit(exit_pstree2);② Makefile
obj-m := pstree2.o KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean: rm -rf *.o .*.cmd *.ko *.mod.c