日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux学习之打印进程树

發(fā)布時間:2025/3/15 linux 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux学习之打印进程树 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

繼續(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ǔ)知識補充:
問題解決需要分析兩個問題:

  • 如何得到系統(tǒng)進程樹1號進程信息
  • 如何由1號進程開始獲取所有進程關(guān)聯(lián)關(guān)系
  • 指導書上,給了2種方法解決:

  • 訪問/proc目錄:用戶空間編程
  • 訪問task_struct結(jié)構(gòu):內(nèi)核空間編程
  • 接下來分別從兩個進行實現(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文件

    #include <linux/input.h> #include <linux/sched.h> #include <linux/unistd.h> #include <linux/list.h> #include <linux/init.h> #include <linux/module.h>//任何模塊程序的編寫都需要包含linux/module.h這個頭文件 #include <linux/kernel.h> 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鏈上的某一節(jié)點作為sibling賦給task_struct即 pstreea(t,b+1); //實現(xiàn)了向children節(jié)點的移動 } } 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()函數(shù)(不要習慣性地寫成printf),printk()函數(shù)是由Linux內(nèi)核定義的,功能與printf相似。字符串<1>表示消息的優(yōu)先級,printk()的一個特點就是它對于不同優(yōu)先級的消息進行不同的處理,之所以要在這兒使用高優(yōu)先級,是因為默認優(yōu)先級的消息可能不能顯示在控制臺上。這個問題就不詳細講了,可以用man命令尋求幫助。 } module_init(pstree_init); module_exit(pstree_exit);//函數(shù)init ()和函數(shù)exit ( )是模塊編程中最基本的也是必須的兩個函數(shù)。 //init ()向內(nèi)核注冊模塊所提供的新功能; //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

    內(nèi)核模塊修改與添加比較復雜,不再開發(fā)。

    基于JavaFX實現(xiàn)進程樹

    你以為結(jié)束了嘛,不!還沒有,接下來用Java和JavaFx實現(xiàn)一個GUI版的進程樹,思路源自方法一,只貼源碼,不再解釋。
    本代碼原創(chuàng),轉(zhuǎn)載請注明!
    基于JavaFX的Linux進程樹

    總結(jié)

    思路簡單,遞歸比較麻煩一些,學到了很多東西,收益匪淺。=w=

    總結(jié)

    以上是生活随笔為你收集整理的Linux学习之打印进程树的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。