通过打印进程控制块中的信息深刻认识进程:

通过打印进程控制块中的信息深刻认识进程:

一、通过top查看进程信息:

通过在终端输入top命令可查看动态的进程信息,查看结果截图如下所示:
在这里插入图片描述
进阶版:
使用man指令可更方便高效;

二、探索并打印task_struct字段中信息:

2.1、打印task_struct字段(初试):

​ 在task_struct字段中包含着与进程相关的大部分信息,该字段被定义在linux/sched.h文件中的721行,在通过阅读csdn中相关文章后,打算打印若干个task_struct字段中的部分信息,代码如下:

内核模块代码

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>   //task结构体
#include <linux/fdtable.h>      //files
#include <linux/fs_struct.h>   //fs
#include <linux/mm_types.h>   //打印内存信息
#include <linux/init_task.h>   
#include <linux/types.h>
#include <linux/atomic.h>
#include "policy.h"
#include "state.h"
MODULE_LICENSE("GPL");  //许可证

//入口函数
static int __init print_pcb(void)    //init宏是由init.h文件所支持
{
        struct task_struct *task,*p;
        struct list_head *pos;   //双向链表
        int count=0;          //统计当前系统进程一共有多少个
 
        printk("\n-----------------------------------------\n打印个进程信息瞅瞅捏!\n\n");
 
        //对链表遍历时,希望从第一个开始
        task=&init_task;  //指向0号进程pcb
 		//遍历操作,使用pos指向,传入的参数task指向tasks字段.0号进程的tasks进行遍历。tasks将所有的进程连在一块。
        list_for_each(pos,&task->tasks) 
        {
                p=list_entry(pos,struct task_struct,tasks); 
                //找到一个节点,就可以用这个节点的tasks字段,找到这个结构体的地址.对应的字段tasks
                //此时的p指针已经指向task_struct结构体的首部,后面就可以通过p指针进行操作
                count++;  //找到一个进程,自加
                printk("\n\n");
                printk("pid号: %d\t进程名:%s\t状态: %s\t动态优先级: %d\t静态优先级: %d\t
                       实时优先级: %d\t父进程pid: %d\tCPU: %d\t调度策略: %s\n ",
                        p->pid,p->comm,tran_state(p->__state), p->prio,p->static_prio,p->rt_priority, 							(p->parent)->pid,task_cpu(p),tran_policy(p->policy));
        
        }
        printk("进程的个数:%d\n",count);
        return 0;
}
 
static void __exit exit_pcb(void)    //出口函数
{
        printk("俺溜咧~~\n");
}
 
// 指明入口点与出口点,入口/出口点是由module.h支持的
 
module_init(print_pcb);
module_exit(exit_pcb);

通过tran_policy()函数将p->policy输出的unsigned int型参数转为具体的宏,便于观看信息;

/*
 * Scheduling policies
 */
/*
 * #define SCHED_NORMAL		0
 * #define SCHED_FIFO		1
 * #define SCHED_RR		2
 * #define SCHED_BATCH		3
 */
/* SCHED_ISO: reserved but not implemented yet */
/*
 * #define SCHED_IDLE		5
 */
/* Can be ORed in to make sure the process is reverted back to SCHED_NORMAL on fork */
/* 
 * #define SCHED_RESET_ON_FORK     0x40000000
 */

char *tran_policy(unsigned int policy)
{
	char *cp;

	switch (policy) {
	case 0:
		cp = "SCHED_NORMAL";
		break;
	case 1:
		cp = "SCHED_FIFO";
		break;
	case 2:
		cp = "SCHED_RR ";
		break;
	case 3:
		cp = "SCHED_BATCH";
		break;
	case 5:
		cp = "SCHED_IDLE";
		break;
	case 0x40000000:
		cp = "SCHED_RESET_ON_FORK";
		break;
	default:
		cp = "unknown";
		break;
	}

	return cp;
}

通过tran_state()函数将p->__state输出的volatile long型参数转为具体的宏,便于观看信息;

/*
 * #define TASK_RUNNING		0
 * #define TASK_INTERRUPTIBLE	1
 * #define TASK_UNINTERRUPTIBLE	2
 * #define __TASK_STOPPED		4
 * #define __TASK_TRACED		8
 */
/* in tsk->exit_state */
/*
 * #define EXIT_ZOMBIE		16
 * #define EXIT_DEAD		32
 */
/* in tsk->state again */
/*
 * #define TASK_DEAD		64
 * #define TASK_WAKEKILL		128
 * #define TASK_WAKING		256
 * #define TASK_PARKED		512
 */

char *tran_state(volatile long state)
{
	char *s = NULL;

	switch (state) {
	case 0:
		s = "TASK_RUNNING      ";
		break;

	case 1:
		s = "TASK_INTERRUPTIBLE";
		break;

	case 2:
		s = "TASK_UNINTERRUPTIBLE";
		break;
	case 4:
		s = "__TASK_STOPPED";
		break;
	case 8:
		s = "__TASK_TRACED";
		break;
	case 16:
		s = "EXIT_ZOMBIE";
		break;
	case 32:
		s = "EXIT_DEAD";
		break;
	case 64:
		s = "TASK_DEAD";
		break;
	case 128:
		s = "TASK_WAKEKILL";
		break;
	case 256:
		s = "TASK_WAKING";
		break;
	case 512:
		s = "TASK_PARKED";
		break;
	default:
		s = "unknown           ";
		break;
	}

	return s;
}

​ 此处为Makefile文件:

obj-m:=pr_task_info.o
CURRENT_PATH:=$(shell pwd)
LINUX_KERNEL:=$(shell uname -r)
LINUX_KERNEL_PATH:=/usr/src/linux-headers-$(LINUX_KERNEL)

all:
	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules

clean:
	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean

​输出打印结果:
在这里插入图片描述
在这里插入图片描述

2.2、打印task_struct字段(再试,加入传参功能):

​ 为了实现打印指定进程的指定信息,我在2.1中代码的基础上做了升级,可以通过传递模块参数processnunm及flag;其中processnum是特定进程的pid号,flag是自己设置的标志位,0代表打印全部进程信息,1代表打印指定进程的所有子进程的部分信息,2代表打印指定进程的所有兄弟进程的部分信息

2.2.1、task_struct 中 parent/children/sibling 三者的关系:

在打印特定进程的子进程信息中,发现需要用到sibling链表,所以对 parent/children/sibling 三者的关系产生了疑惑。在《深入理解Linux内核》中有如下一图表明 task_struct 中 parent/children/sibling 三者的关系:

  • sibling.next指向进程的下一个兄弟进程的进程描述符sibling成员,若其后没有其他兄弟进程,则指向父进程;而sibling.prev指向进程的上一个兄弟进程,若其之前没有兄弟进程,则指向父进程。
  • children.next指向父进程的第一个子进程的sibling成员(而不是children成员!),而children.prev却指向父进程的最后一个子进程的sibling成员。
  • 特别注意children.next指向的是sibling成员,因此在使用list_entry()获得task_struct指针时,参数要用sibling而不是children,更不是tasks成员。

​ 所以在下面的代码中是:

list_for_each(pos,&p->children) 
{
        tmp=list_entry(pos,struct task_struct,sibling);
        count++;
        printk("pid号: %ld\t进程名:%s\t状态: %s\t动态优先级: %ld\t静态优先级: %ld\t实时优先级: %ld\t父进程pid: %ld\tCPU: %ld\t调度策略: %s\n",tmp->pid,tmp->comm,tran_state(tmp->__state),tmp->prio,tmp->static_prio,tmp->rt_priority,(tmp->parent)->pid,task_cpu(tmp),tran_policy(tmp->policy));
}
2.2.2、代码:

​ 如下是功能增加后的代码部分,其中state.h及policy.h和2.1中一致,此处不做展示

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>   //task结构体
#include <linux/fdtable.h>      //files
#include <linux/fs_struct.h>   //fs
#include <linux/mm_types.h>   //打印内存信息
#include <linux/init_task.h>   
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/atomic.h>
#include "policy.h"
#include "state.h"
MODULE_LICENSE("GPL");  //许可证


static int processnum = 156;
module_param(processnum, int, 0644);
MODULE_PARM_DESC(processnum, "give a pid num to print infomation of it's children/sibling!");

static int flag = 0;
module_param(flag, int, 0644);
MODULE_PARM_DESC(flag, "1 means children and 2 means sibling ");


void print(struct task_struct *p )
{
    printk("pid号: %d\t进程名:%s\t\t状态: %s\t动态优先级: %d\t静态优先级: %d\t实时优先级: %d\t\t父进程pid: %d\tCPU: %d\t调度策略: %s\n",
                p->pid,p->comm,tran_state(p->__state),p->prio,p->static_prio,p->rt_priority,(p->parent)->pid,task_cpu(p),tran_policy(p->policy));
}

//入口函数
static int __init print_pcb(void)    //init宏是由init.h文件所支持
{
        struct task_struct *task,*p,*tmp,*q;
        struct list_head *pos;
        //struct task_struct *p = pid_task(find_vpid(processnum),PIDTYPE_PID);//通过find_task_by_vpid(函数找到pid对应进程的task_struct结构体)
        int count =0;
        printk("\n-----------------------------------------\n打印个进程信息瞅瞅捏!\n\n");
        
        if(flag==0){
                task=&init_task;  //指向0号进程pcb
                printk("全部进程信息:\n");
                list_for_each(pos,&task->tasks) 
                {
                        p=list_entry(pos,struct task_struct,tasks); 
                        //找到一个节点,就可以用这个节点的tasks字段,找到这个结构体的地址.对应的字段tasks
                        //此时的p指针已经指向task_struct结构体的首部,后面就可以通过p指针进行操作
                        count++;  //找到一个进程,自加
                        print(p);
                }
                printk("进程的个数:%d\n",count);
                return 0;
        }
        
        p=pid_task(find_get_pid(processnum),PIDTYPE_PID);//找到processnum号对应的task_struct结构体;
        print(p);
        if(flag==1){
                 printk("%d进程%s的全部子进程信息:\n",processnum,p->comm);
                 list_for_each(pos,&p->children) 
                {
                        tmp=list_entry(pos,struct task_struct,sibling);
                        //找到一个节点,就可以用这个节点的tasks字段,找到这个结构体的地址.对应的字段tasks
                        //此时的p指针已经指向task_struct结构体的首部,后面就可以通过p指针进行操作
                        count++;  //找到一个进程,自加
                        print(tmp);
                }
                 printk("进程%s子进程个数:%d\n",p->comm,count);
         }
         else if(flag==2){
                printk("%d进程%s的全部兄弟进程信息:\n",processnum,p->comm);
                q=p;
                p=p->parent;
                list_for_each(pos,&p->children) 
                {
                        tmp=list_entry(pos,struct task_struct,sibling); 
                        //找到一个节点,就可以用这个节点的tasks字段,找到这个结构体的地址.对应的字段tasks
                        //此时的p指针已经指向task_struct结构体的首部,后面就可以通过p指针进行操作
                        count++;  //找到一个进程,自加
                        print(tmp);
                }
                printk("进程%s的兄弟进程个数:%d\n",q->comm,count);
         }
        return 0;
}
 
static void __exit exit_pcb(void)    //出口函数
{
        printk("俺溜咧~~\n");
}
 
// 指明入口点与出口点,入口/出口点是由module.h支持的
 
module_init(print_pcb);
module_exit(exit_pcb);
2.2.2、调试部分:

​ 将写好的代码文件make编译

在这里插入图片描述

​ 在插入模块之前,首先要找到要打印进程的pid号用来传参。为了方便展示,先通过pstree命令来找到有子进程或有兄弟进程的进程,再通过top命令和管道工具找到该进程的进程号。调试部分如下:

在这里插入图片描述

在这里插入图片描述

找到两个进程systemed、vmware-vmblock-的进程树及其对应的进程号:

在这里插入图片描述

​ 插入模块,并传参:

  • flag置为0,打印所有进程信息:
xhb@xhb-virtual-machine:~/mycode/test8$ sudo insmod pr_task_info.ko flag=0

在这里插入图片描述

在这里插入图片描述

  • 打印systemed的子进程相关信息:
xhb@xhb-virtual-machine:~/mycode/test8$ sudo insmod pr_task_info.ko processnum=1 flag=1

在这里插入图片描述

在这里插入图片描述

  • 在打印systemed子进程中我们看到其中一个pid号为485的进程vmware-vmblock-,打印该进程的兄弟进程相关信息:
xhb@xhb-virtual-machine:~/mycode/test8$ sudo insmod pr_task_info.ko processnum=485 flag=2

在这里插入图片描述

在这里插入图片描述

  • 打印bash的子进程及sh的兄弟进程:
xhb@xhb-virtual-machine:~/mycode/test8$ sudo insmod pr_task_info.ko processnum=1901 flag=1
xhb@xhb-virtual-machine:~/mycode/test8$ sudo insmod pr_task_info.ko processnum=1946 flag=2

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

三、代码中涉及到的源码分析:

更新中…

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值