news 2026/6/11 14:15:56

操作系统课程设计:3-打印进程树

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
操作系统课程设计:3-打印进程树

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
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 6:33:01

13、利用 Windows 环境开发应用程序的技术指南

利用 Windows 环境开发应用程序的技术指南 在现代软件开发中,充分利用操作系统提供的环境和功能可以显著提升应用程序的性能和用户体验。本文将深入介绍 Windows 系统中一些重要的开发技术,包括 PlayToManager 初始化、后台任务的实现与注册、搜索合约的集成等内容。 1. Pl…

作者头像 李华
网站建设 2026/6/10 14:13:35

19、数据管理全解析:从本地到远程的数据处理方案

数据管理全解析:从本地到远程的数据处理方案 在应用开发中,数据管理是至关重要的一环。本文将详细介绍不同类型数据的管理方式,包括会话状态、临时数据、用户数据,以及如何使用 SQLite 数据库和获取远程数据。 1. 会话状态管理 会话状态的保存和恢复是确保应用在不同状态…

作者头像 李华
网站建设 2026/6/10 15:59:22

科学提升孩子记忆力的五大方法

作为家长&#xff0c;你是否经常为孩子“记不住”知识点而焦虑&#xff1f;明明反复背诵&#xff0c;考试时却一片空白。其实&#xff0c;这并非孩子不够努力&#xff0c;而是记忆方法出了偏差。根据脑科学研究&#xff0c;人类大脑的记忆潜力远超想象&#xff0c;关键在于激活…

作者头像 李华
网站建设 2026/6/11 2:24:14

免费音频转换利器:用fre:ac解决你的音频处理难题

免费音频转换利器&#xff1a;用fre:ac解决你的音频处理难题 【免费下载链接】freac The fre:ac audio converter project 项目地址: https://gitcode.com/gh_mirrors/fr/freac 你是不是也遇到过这样的困扰&#xff1a;下载的音乐格式不兼容播放器&#xff0c;收藏的CD想…

作者头像 李华
网站建设 2026/6/11 2:30:37

三维重建终极指南:Astra Toolbox快速上手完整教程

三维重建终极指南&#xff1a;Astra Toolbox快速上手完整教程 【免费下载链接】astra-toolbox ASTRA Tomography Toolbox 项目地址: https://gitcode.com/gh_mirrors/as/astra-toolbox Astra Toolbox 是一款功能强大的开源三维重建与断层扫描工具箱&#xff0c;专为医学…

作者头像 李华
网站建设 2026/6/10 14:10:32

施工企业必看:红圈跟明建云哪个好?全面功能与核心优势大揭秘

面对堆积如山的报表、散落各处的数据,以及瞬息万变的市场,施工企业的管理者们常常感到力不从心:项目成本究竟是否可控?资金流是否安全?经营风险藏在哪里?选择一款真正懂业务、能落地的数字化管理系统,已不再是“锦上添花”,而是关乎企业生存与高质量发展的“必修课”。在众多…

作者头像 李华