[关闭]
@1405010312 2016-04-10T12:43:55.000000Z 字数 4574 阅读 1792

Linux 系统分析实验报告

Linux 系统分析实验报告

实验四丶系统进程树实验

报告提交日期: 2016.4.10          报告提交截止日期: 2016.4.11
姓名:肖罗罗          学号:1405010312          班级:计算机三班


一.实验题目

  • 在虚拟linux系统环境下用两种方法打印linux进程树

二.实验目的

了解task_struct结构及进程管理结构
了解kernel链表遍历结构
学会linux内核模块编译

三.实验平台

VM虚拟机下linux系统

四.实验要求

  1. 在Linux下,用C语言自己编写一个程序,要求能以树状结构(即能体现父子关系)打印出系统当前的所有进程
  2. 独立完成

五.流程

方法一

实验思路
  • 本实验通过采用在用户态运行C程序来打印进程树.linux系统的/proc目录下有一组以进程号pid为目录名的文件,每个目录包含了进程的具体信息,于是通过/proc目录获得各进程的父进程ppid,获得进程树的父亲表示,再将进程树的父亲表示转换成左孩子与右孩子,最后中序遍历二叉树,输出进程信息即可。
流程

1.创建 pstree.c 文件(其主要结构如下)

  1. typedef struct file_info{
  2. int pid;
  3. int ppid;
  4. char name[1024];
  5. int flag;
  6. int rec;
  7. }info;
  8. my_getpid(char* str);
  9. my_getppid(char* str);
  10. child_exist(info* file, int count, int ppid);
  11. print_pstree(info* file, int count, int ppid, int rec)

2.用 gcc 工具编译源文件

  1. gcc -o pstree pstree.c

3.运行编译好的可执行文件

./pstree
实验运行结果

实验运行结果
图一.实验运行结果

源代码
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<errno.h>
  4. #include<string.h>
  5. #include<sys/types.h>
  6. #include<netdb.h>
  7. #include<pthread.h>
  8. #include<unistd.h>
  9. #include<dirent.h>
  10. char default_path[1024]="/proc/";
  11. int s=0;
  12. typedef struct file_info
  13. {
  14. int pid; // 进程号
  15. int ppid; // 父进程号
  16. char name[1024]; // 进程名称
  17. int flag; //进程标志
  18. int rec; //打印进程树时用来标志是几级进程的
  19. }info;
  20. int my_getpid(char *str) // 获得进程号
  21. {
  22. int len=strlen(str);
  23. char num[10];
  24. int i,j,ret;
  25. if(strncmp(str,"Pid",3)==0)
  26. {
  27. for(i=0;i<len;i++)
  28. {
  29. if(str[i]>='0'&&str[i]<='9')
  30. break;
  31. }
  32. for(j=0;j<len-i;j++)
  33. {
  34. num[j]=str[i+j];
  35. }
  36. ret=atoi(num);
  37. }
  38. else ret=0;
  39. return ret;
  40. }
  41. int my_getppid(char *str) // 获得父进程号
  42. {
  43. int len=strlen(str);
  44. char num[10];
  45. int i,j,ret;
  46. if(strncmp(str,"PPid",4)==0)
  47. {
  48. for(i=0;i<len;i++)
  49. {
  50. if(str[i]>='0'&&str[i]<='9')
  51. break;
  52. }
  53. for(j=0;j<len-i;j++)
  54. {
  55. num[j]=str[i+j];
  56. }
  57. ret=atoi(num);
  58. }
  59. else ret=0;
  60. return ret;
  61. }
  62. int child_exist(info *file,int count,int ppid) //判断是否存在子进程
  63. {
  64. int i;
  65. for(i=0;i<count;i++)
  66. {
  67. if(file[i].flag==0&&file[i].ppid==ppid)
  68. return 1;
  69. }
  70. return 0;
  71. }
  72. void print_pstree(info *file,int count,int ppid,int rec) // 打印进程树,用递归方法,中序遍历
  73. {
  74. int i,j,k;
  75. for(i=0;i<count;i++)
  76. {
  77. if(file[i].flag==0&&file[i].ppid==ppid)
  78. {
  79. file[i].rec=rec+1;
  80. file[i].flag=1;
  81. for(k=0;k<rec;k++)
  82. printf(" ");
  83. printf("%s\n",file[i].name);
  84. print_pstree(file,count,file[i].pid,file[i].rec);
  85. }
  86. }
  87. }
  88. int main()
  89. {
  90. int i,j,k,total,s1,s2,count,t;
  91. char str[1024],dir[1024];
  92. struct dirent **namelist;
  93. strcpy(dir,default_path);
  94. total=scandir(dir,&namelist,0,alphasort);
  95. printf("path=%s,total=%d\n",dir,total);
  96. for(i=0;i<total;i++)
  97. {
  98. strcpy(str,namelist[i]->d_name);
  99. if(str[0]>='0'&&str[0]<='9')
  100. count++;
  101. }
  102. printf("进程数:%d\n",count);
  103. info file[1024];
  104. i=0;
  105. t=0;
  106. while(i<total)
  107. {
  108. FILE *fp;
  109. char path[1024],name[1024];
  110. int pid,ppid;
  111. strcpy(str,namelist[i]->d_name);
  112. strcpy(path,default_path);
  113. if(str[0]>='0'&&str[0]<='9')
  114. {
  115. strcat(path,str);
  116. strcat(path,"/status");
  117. fp=fopen(path,"r");
  118. while(!feof(fp))
  119. {
  120. fgets(str,1024,fp);
  121. //pid
  122. if((s1=my_getpid(str))!=0)
  123. pid=s1;
  124. //ppid
  125. if((s2=my_getppid(str))!=0)
  126. ppid=s2;
  127. //name
  128. if(strncmp(str,"Name",4)==0)
  129. {
  130. for(j=4;j<strlen(str);j++)
  131. {
  132. if(str[j]>='a'&&str[j]<='z')
  133. break;
  134. }
  135. for(k=j;k<strlen(str);k++)
  136. {
  137. name[k-j]=str[k];
  138. }
  139. name[k-j-1]='\0';
  140. }
  141. file[t].pid=pid;
  142. file[t].ppid=ppid;
  143. strcpy(file[t].name,name);
  144. }
  145. fclose(fp);
  146. t++;
  147. }
  148. i++;
  149. }
  150. memset(&file->flag,0,count);
  151. memset(&file->rec,0,count);
  152. print_pstree(file,count,0,0);
  153. }
实验体会

①.在写代码的时候太粗心刚开始进程树没打印出来.
②.进程树不熟悉,经常出现各类错误

方法二

实验思路
  1. 每个进程都有一个task_struct结构
  2. 包括进程之间的族系成员关系(如下)
  1. pid_t pid;
  2. struct task_struct* parent;
  3. struct list_head children;
  4. struct list_head sibling;
  5. char comm[16]; ///进程名称
流程
  1. 编译 make
  2. 安装模块 sudo insmod pstree.ko
  3. 运行模块 sudo dmesg
  4. 删除模块 sudo rmmod pstree
源代码

makefile

  1. ifneq ($(KERNELRELEASE),)
  2. obj-m:=pstree2.o
  3. else
  4. KDIR := /lib/modules/$(shell uname -r)/build
  5. PWD:=$(shell pwd)
  6. all:
  7. make -C $(KDIR) M=$(PWD) modules
  8. clean:
  9. make -C $(KDIR) M=$(PWD) clean
  10. endif

pstree.c

  1. #ifndef __KERNEL__
  2. #define __KERNEL__
  3. #endif
  4. #ifndef MODULE
  5. #define MODULE
  6. #endif //首先明确这是一个模块
  7. #include <linux/sched.h>
  8. #include <linux/unistd.h>
  9. #include <linux/list.h>
  10. #include <linux/init.h>
  11. #include <linux/module.h>//任何模块程序的编写都需要包含linux/module.h这个头文件
  12. #include <linux/kernel.h>
  13. MODULE_LICENSE("Dual BSD/GPL");//
  14. void pstreea(struct task_struct* p,int b){
  15. int i;
  16. for(i=1;i<=b;i++)
  17. printk(" ");
  18. printk("|--%s\n",p->comm);
  19. struct list_head* l;
  20. for (l = p->children.next; l!= &(p->children);l = l->next){
  21. //作用同list_for_each()
  22. struct task_struct*t=list_entry(l,struct task_struct,sibling);//将children链上的某一节点作为sibling赋给task_struct即
  23. pstreea(t,b+1); //实现了向children节点的移动
  24. }
  25. }
  26. static int pstree_init(void){
  27. struct task_struct* p;
  28. int b=0;
  29. for ( p = current; p != &init_task; p = p->parent ) ;//回溯到初始父进程
  30. pstreea(p,b);
  31. return 0;
  32. }
  33. static void pstree_exit(void){
  34. printk("Hello, kernel!/n"); //注意在这儿使用的是printk()函数(不要习惯性地写成printf),printk()函数是由Linux内核定义的,功能与printf相似。字符串<1>表示消息的优先级,printk()的一个特点就是它对于不同优先级的消息进行不同的处理,之所以要在这儿使用高优先级,是因为默认优先级的消息可能不能显示在控制台上。这个问题就不详细讲了,可以用man命令寻求帮助。
  35. }
  36. module_init(pstree_init);
  37. module_exit(pstree_exit);//函数init ()和函数exit ( )是模块编程中最基本的也是必须的两个函数。
  38. //init ()向内核注册模块所提供的新功能;
  39. //exit ()负责注销所有由模块注册的功能。
实验体会
  1. 忘记要先编译内核才能开始该实验
  2. makefile格式错误
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注