继续Linux的学习,操作系统学到了Linux系统下的进程结构,布置了一个作业是打印进程树,来加深一下对Linux进程的理解。
虚拟主机
主机:联想Y7000P;64位windows10;CPU:i7-9750H;显卡:GTX 1660 Ti;内存:16G
虚拟机:Ubuntu 18.04 LTS;硬盘100G;内存4G;64位;4核心
Linux内核:5.11.8
本博客原创,转载请注明!!!
基础知识补充:
问题解决需要分析两个问题:
指导书上,给了2种方法解决:
接下来分别从两个进行实现
有关/proc文件可以看一下这个
参考资料:linux proc目录详解
源码参考:
系统进程树实验
把指导书的源码copy一下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
char default_path[1024]="/proc/";
int s=0;
typedef struct file_info
{
int pid; // 进程号
int ppid; // 父进程号
char name[1024]; // 进程名称
int flag; //进程标志
int rec; //打印进程树时用来标志是几级进程的
}info;
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='0'&&str[i]<='9')
break;
}
for(j=0;j='0'&&str[i]<='9')
break;
}
for(j=0;jd_name);
if(str[0]>='0'&&str[0]<='9')
count++;
}
printf("进程数:%d\n",count);
info file[1024];
i=0;
t=0;
while(id_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);
//pid
if((s1=my_getpid(str))!=0)
pid=s1;
//ppid
if((s2=my_getppid(str))!=0)
ppid=s2;
//name
if(strncmp(str,"Name",4)==0)
{
for(j=4;j='a'&&str[j]<='z')
break;
}
for(k=j;kflag,0,count);
memset(&file->rec,0,count);
print_pstree(file,count,0,0);
}
这上边是指导书的源码,大概看了一下。讲解一下思路:
整体思路就是一直遍历/proc目录下的进程文件中的status文件,然后暴力获取获取ppid。
根据它的思路,二次开发一下,此代码原创,转载请注明!,如有bug,请告知,谢谢。
#include
#include
#include
#include
#include //Windows中没有这个头文件
#include
#define MAX_PROC_NUM 1024
#define MAX_PROC_NAME_LEN 254
#define ROOT_FILE "/proc"
struct procInfo
{
char name[MAX_PROC_NAME_LEN]; //进程的名字
int pid; //进程id
int ppid; //进程的父进程
int floor; //递归的层次
}procs[MAX_PROC_NUM];
int procNum = 0;
int max_floor = 0;
//int atoi(const char *nptr); 把str变成int
void getProcStatus(const char* str,struct procInfo *proc)
{
FILE *fp; //获取状态信息
char t_title[MAX_PROC_NAME_LEN];
char t_info[MAX_PROC_NAME_LEN];
fp = fopen(str,"r");
if(fp == NULL)
{
strcpy(proc->name,"NULL");
proc->ppid = -1;
proc->floor = -1;
}
else
{
while( fscanf(fp,"%s",t_title) != EOF )
{
if(strncmp(t_title,"Name:",5) == 0)
{
fscanf(fp,"%s",proc->name);
}
else if(strncmp(t_title,"PPid:",5) == 0)
{
fscanf(fp,"%s",t_info);
proc->ppid = atoi(t_info);
}
}
}
fclose(fp);
}
void readDirInfo(const char *str)
{
DIR *dir;
struct dirent *ptr;
int tmpLength=0;
dir = opendir(str); //打开一个目录
char procStatusString[64];
//开始获取当前有多少个进程
while( (ptr = readdir(dir)) != NULL )
{
tmpLength = strlen((ptr->d_name));
int i=0;
for(i=0;id_name)[i] <'0' || (ptr->d_name)[i] > '9' )
break;
}
if(i == tmpLength)
{
procs[procNum].pid = atoi(ptr->d_name);
sprintf(procStatusString,"%s/%d/status",ROOT_FILE,procs[procNum].pid);
//开始获取Status中的信息
getProcStatus(procStatusString,&(procs[procNum]));
procNum++;
}
if(procNum >= MAX_PROC_NUM)
break;
}
closedir(dir);
}
void GetProcTree(int pid,int step)
{
for(int i=0;i
我一共定义了3个子函数,其中void GetProcTree(int pid,int step)
是递归用来打印进程树的,而void readDirInfo(const char *str)
函数是用来获取/proc
目录下的文件夹信息的,void getProcStatus(const char* str,struct procInfo *proc)
是读取每个文件夹中的Status
文件的。
执行完readDirInfo(ROOT_FILE);
这句,就获取了系统目录/proc
下的所有Status
信息,然后用GetProcTree()
函数来递归打印进程树。
在Linux系统下,每一个进程都有一个task_struct结构体,包括进程之间的族系成员关系
pid_t pid;
struct task_struct* parent;
struct list_head children;
struct list_head sibling;
char comm[16]; ///进程名称
大体思路:
先找到根进程for(cur=current; cur->pid!=1; cur = cur->parent)
;然后用深度优先算法(BFS)递归打印子进程。结束的标志:子进程pid=0
;
直接看源码:
myPsTree.c
文件
#include
#include
#include
#include
#include
#include //任何模块程序的编写都需要包含linux/module.h这个头文件
#include
MODULE_LICENSE("Dual BSD/GPL");//
void pstreea(struct task_struct* p,int b){
int i;
for(i=1;i<=b;i++)
printk(" ");
printk("|--%s\n",p->comm);
struct list_head* l;
for (l = p->children.next; l!= &(p->children);l = l->next){
//作用同list_for_each()
struct task_struct*t=list_entry(l,struct task_struct,sibling);//将children链上的某一节点作为sibling赋给task_struct即
pstreea(t,b+1); //实现了向children节点的移动
}
}
static int pstree_init(void){
struct task_struct* p;
int b=0;
for ( p = current; p != &init_task; p = p->parent ) ;//回溯到初始父进程
pstreea(p,b);
return 0;
}
static void pstree_exit(void){
printk("Hello, kernel!/n"); //注意在这儿使用的是printk()函数(不要习惯性地写成printf),printk()函数是由Linux内核定义的,功能与printf相似。字符串<1>表示消息的优先级,printk()的一个特点就是它对于不同优先级的消息进行不同的处理,之所以要在这儿使用高优先级,是因为默认优先级的消息可能不能显示在控制台上。这个问题就不详细讲了,可以用man命令寻求帮助。
}
module_init(pstree_init);
module_exit(pstree_exit);//函数init ()和函数exit ( )是模块编程中最基本的也是必须的两个函数。
//init ()向内核注册模块所提供的新功能;
//exit ()负责注销所有由模块注册的功能。
Makefile
文件
obj-m:=myPsTree.o
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
然后安装过程和之前一样
编译 make
安装模块 sudo insmod pstree.ko
运行模块 sudo dmesg
删除模块 sudo rmmod pstree
内核模块修改与添加比较复杂,不再开发。
你以为结束了嘛,不!还没有,接下来用Java和JavaFx实现一个GUI版的进程树,思路源自方法一,只贴源码,不再解释。
本代码原创,转载请注明!
基于JavaFX的Linux进程树
思路简单,递归比较麻烦一些,学到了很多东西,收益匪浅。=w=