Linux学习之打印进程树
前言
繼續(xù)Linux的學習,操作系統(tǒng)學到了Linux系統(tǒng)下的進程結(jié)構(gòu),布置了一個作業(yè)是打印進程樹,來加深一下對Linux進程的理解。
虛擬主機
主機:聯(lián)想Y7000P;64位windows10;CPU:i7-9750H;顯卡:GTX 1660 Ti;內(nèi)存:16G
虛擬機:Ubuntu 18.04 LTS;硬盤100G;內(nèi)存4G;64位;4核心
Linux內(nèi)核:5.11.8
本博客原創(chuàng),轉(zhuǎn)載請注明!!!
基礎(chǔ)知識補充:
問題解決需要分析兩個問題:
指導書上,給了2種方法解決:
接下來分別從兩個進行實現(xiàn)
訪問/proc目錄
有關(guān)/proc文件可以看一下這個
參考資料:linux proc目錄詳解
源碼參考:
系統(tǒng)進程樹實驗
指導書源碼
把指導書的源碼copy一下:
#include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<sys/types.h> #include<netdb.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; 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; } 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; } 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;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++)printf(" ");printf("%s\n",file[i].name);print_pstree(file,count,file[i].pid,file[i].rec);}} } 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);for(i=0;i<total;i++){strcpy(str,namelist[i]->d_name);if(str[0]>='0'&&str[0]<='9')count++;}printf("進程數(shù):%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);//pidif((s1=my_getpid(str))!=0)pid=s1;//ppidif((s2=my_getppid(str))!=0)ppid=s2;//nameif(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++;}memset(&file->flag,0,count);memset(&file->rec,0,count);print_pstree(file,count,0,0); }這上邊是指導書的源碼,大概看了一下。講解一下思路:
整體思路就是一直遍歷/proc目錄下的進程文件中的status文件,然后暴力獲取獲取ppid。
二次開發(fā)
根據(jù)它的思路,二次開發(fā)一下,此代碼原創(chuàng),轉(zhuǎn)載請注明!,如有bug,請告知,謝謝。
#include <stdio.h> #include <stdlib.h> #include <string.h>#include <sys/types.h> #include <dirent.h> //Windows中沒有這個頭文件 #include <unistd.h>#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; //進程idint ppid; //進程的父進程int floor; //遞歸的層次 }procs[MAX_PROC_NUM];int procNum = 0; int max_floor = 0;//int atoi(const char *nptr); 把str變成intvoid getProcStatus(const char* str,struct procInfo *proc) {FILE *fp; //獲取狀態(tài)信息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;i<tmpLength;i++){if((ptr->d_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<procNum;i++){if(procs[i].ppid == pid){procs[i].floor = step;for(int j=0;j<step;j++)printf(" ");printf("%s\n",procs[i].name);GetProcTree(procs[i].pid,step+1);}} }int main() {readDirInfo(ROOT_FILE);GetProcTree(0,0);printf("procNum = %d\n",procNum);return 0; }我一共定義了3個子函數(shù),其中void GetProcTree(int pid,int step)是遞歸用來打印進程樹的,而void readDirInfo(const char *str)函數(shù)是用來獲取/proc目錄下的文件夾信息的,void getProcStatus(const char* str,struct procInfo *proc)是讀取每個文件夾中的Status文件的。
執(zhí)行完readDirInfo(ROOT_FILE);這句,就獲取了系統(tǒng)目錄/proc下的所有Status信息,然后用GetProcTree()函數(shù)來遞歸打印進程樹。
訪問PCB結(jié)構(gòu)方案
task_struct結(jié)構(gòu)
在Linux系統(tǒng)下,每一個進程都有一個task_struct結(jié)構(gòu)體,包括進程之間的族系成員關(guān)系
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);然后用深度優(yōu)先算法(BFS)遞歸打印子進程。結(jié)束的標志:子進程pid=0;
指導書源碼
直接看源碼:
myPsTree.c文件
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內(nèi)核模塊修改與添加比較復雜,不再開發(fā)。
基于JavaFX實現(xiàn)進程樹
你以為結(jié)束了嘛,不!還沒有,接下來用Java和JavaFx實現(xiàn)一個GUI版的進程樹,思路源自方法一,只貼源碼,不再解釋。
本代碼原創(chuàng),轉(zhuǎn)載請注明!
基于JavaFX的Linux進程樹
總結(jié)
思路簡單,遞歸比較麻煩一些,學到了很多東西,收益匪淺。=w=
總結(jié)
以上是生活随笔為你收集整理的Linux学习之打印进程树的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: gps频率范围_一种应用于低电压GPS接
- 下一篇: Linux基础学习十:Linux的权限管