前言

继续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

本博客原创,转载请注明!!!

基础知识补充:
问题解决需要分析两个问题:

  1. 如何得到系统进程树1号进程信息
  2. 如何由1号进程开始获取所有进程关联关系

指导书上,给了2种方法解决:

  1. 访问/proc目录:用户空间编程
  2. 访问task_struct结构:内核空间编程

接下来分别从两个进行实现

访问/proc目录

有关/proc文件可以看一下这个
参考资料:linux proc目录详解
源码参考:
系统进程树实验

指导书源码

把指导书的源码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("进程数:%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。

二次开发

根据它的思路,二次开发一下,此代码原创,转载请注明!,如有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;   //获取状态信息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个子函数,其中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()函数来递归打印进程树。

访问PCB结构方案

task_struct结构

在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 <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链上的某一节点作为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

内核模块修改与添加比较复杂,不再开发。

基于JavaFX实现进程树

你以为结束了嘛,不!还没有,接下来用Java和JavaFx实现一个GUI版的进程树,思路源自方法一,只贴源码,不再解释。
本代码原创,转载请注明!
基于JavaFX的Linux进程树

总结

思路简单,递归比较麻烦一些,学到了很多东西,收益匪浅。=w=

Linux学习之打印进程树相关推荐

  1. 模仿pstree 打印进程树

    一个查看进程树的小栗子 可以打印进程数,并且可以加参数 -n 显示pid,加参数-p 显示线程信息 ( 和pstree 的参数并不一样) 准备工作 step 1 处理进程信息 想办法找到所有当前进程 ...

  2. linux查询python的进程树_如何在Linux中查看所有正在运行的进程

    你可以使用ps命令.它能显示当前运行中进程的相关信息,包括进程的PID.Linux和UNIX都支持ps命令,显示所有运行中进程的相关信息.ps命令能提供一份当前进程的快照.如果你想状态可以自动刷新,可 ...

  3. linux查询python的进程树_linux中使用ps命令查看系统进程

    在网上查了很多关于ps的资料,并没有找到英文的全名,通过man ps查看手册,有个这样的字符串SIMPLE PROCESS SELECTION,我姑且认为ps的全名为process selection ...

  4. Linux命令_pstree 显示进程树

    目录 1. 语法 1.1. 常用参数 2. 常见用法 2.1. 指定PID 2.2. 指定用户 2.3. 显示某个进程详细信息 2.4. 指定查看某个进程的父进程 1. 语法 pstree [参数] ...

  5. 基于JavaFX的Linux进程树

    前言 最近学Linux,有一个Linux进程树的作业,本来是有C源码的,为了练手,写了一个Java带有GUI版本的. 本博客和源代码原创,转载请注明! 本文链接 个人博客:https://rongli ...

  6. linux 查看进程树,LINUX 如何查看进程树

    问题 1)以用户zhangsan登入,完成下列操作 2)使用vim打开abc.txt文件,不要编辑.不退出 3)以用户root登入另一个终端,完成下列操作 4)查看整个系统的进程树,输出PID号.完整 ...

  7. linux 进程树id,linux shell杀死进程树

    对于一个互联网运维人员来说,linux的shell命令是一个不得不会的命令,并且杀死一个进程也是经常被使用到的.根据进程id杀死进程用的命令是kill,根据进程名杀死进程的命令是pkill或是kill ...

  8. 进程树--用Enki学Linux系列(18)

    每个进程都是被另一个进程开启(或者fork)的.有一个特殊的根进程(叫做"init")是当你的系统第一次引导时直接被内核开启的. 在这种形式下,运行在系统上进程的集合形成了一个以i ...

  9. linux绘制进程树,Linux基础命令---显示树形进程pstree

    pstree pstree显示正在运行的进程的树形结构,树以PID为根:如果省略了pid则以init为根.如果指定了用户名,则显示根植于该用户拥有的进程的所有进程树.如果pstree被调用为pstre ...

最新文章

  1. 推荐11个实用Python库
  2. BZOJ3670: [Noi2014]动物园
  3. mysql select 子查询_SELECT中常用的子查询操作
  4. 字符指针与字符串变量的转换
  5. 以太坊服务器是什么_今日推荐 | 以太坊独立地址数破亿,持有ETH地址数达4000万意味着什么?...
  6. UUID实现之一twitter的分布式自增IDsnowflake算法
  7. 天翼空间应用商城 App Market体验
  8. Git-版本控制 (二)
  9. 月薪30K大佬源码阅读的经验总结,干货不容错过
  10. BAT批处理自动安装软件
  11. NXP推出Icode DNA芯片,助力产品防伪鉴定
  12. 基于STM32的PWM电机驱动TB6612、A4950
  13. 【华为机考】2022年华为研发人员在线笔试
  14. Science Advances:恐惧学习中内侧前额叶和杏仁核theta振荡同步活动
  15. PowerDesigner修改设置表格背景色
  16. HTML标记由尖括号和,HTML标记
  17. “梧桐杯”中国移动大数据应用创新大赛 - 智慧金融赛道Baseline
  18. (2)Hadoop核心 -- java代码对MapReduce的例子1
  19. 1.swift 常量和变量
  20. 计时函数clock()与数据类型clock_t

热门文章

  1. java jdk安装与环境变量配置
  2. jenkins部署java项目之小白的笔记
  3. 判断数组中是否包含某个字符(contains)
  4. 实用、酷炫的可视化,你用10小时,同事用10分钟,差距在哪?
  5. 30分钟后的飞鸽传书
  6. I-EIM分享一套局域网通讯源码
  7. 无论做哪种演示红色警戒
  8. 飞鸽传书官方网站 2012 最新源码公开
  9. 如何在C++中嵌入JAVA程序
  10. IIS里FTP多用户设置,终于不用Serv-U了,很方便,个人用够用了