我们来动手实践打印进程描述符task_struct中的字段

这里采用的方式是:插入内核模块,使用的方法是:遍历进程链表

一、task_struct初步解读

首先要下载自己的源代码,可以参考我之前的博客https://blog.csdn.net/weixin_45730790/article/details/121294180?spm=1001.2014.3001.5501

可以使用bootlin在线查看源代码

在include文件的目录下打开头文件。task_struct结构位于sched.h头文件中。/include/linux/sched.h

schde.h就显示了pcb在内核中的全貌。涉及的字段非常多。这里只关注pcb本身,并且一些能反映器其结构的字段。

在这里插入图片描述

首先看第一个字段state,也就是它的状态信息,

再往下是它的内核栈*stack

flags是进程的标志

ptrace字段是用来实现断点调试的

在这里插入图片描述

再往下我们可以看到一些优先级信息

prio是动态优先级,static_prio是静态优先级,rt_priority是实时优先级

在这里插入图片描述

再往下可以看到policy字段,这是它的进程调度策略

在这里插入图片描述

再往下可以看到调度器统计进程的运行信息 sched_info ,它也是一个结构体

接下来就是tasks字段,这是一个双向链表。正是这个字段把所有的进程连到了一块,我们才能对进程进行遍历。

在这里插入图片描述

接下来是它的线性地址的所有信息了

active_mm是最后访问的地址空间指针

在这里插入图片描述

这里是进程的亲属关系

打印pcb的重要信息,比如状态信息,优先级信息,亲属关系,文件系统信息以及内存方面的信息。
在这里插入图片描述

这两个信息是用来反映上下文切换的次数的,nvcsw反映主动上下文切换的次数,nivcsw反映被动上下文切换的次数

在这里插入图片描述

这两个字段用来记录缺页统计

在这里插入图片描述

这个数组是相应程序的名字

在这里插入图片描述

这两个字段负责相应进程间的通信

在这里插入图片描述

这里是非常重要的,它的文件信息。fs保存一个指向文件系统信息的指针,files保存一个指向进程文件描述符表的指针

在这里插入图片描述

再往下是它的命名空间

在这里插入图片描述

再往下我们可以看到,这是它的信号描述符了

在这里插入图片描述

再往下我们可以看到,IRQ都是它的中断信息

在这里插入图片描述

内存回收

在这里插入图片描述

记录进程的IO计数

后面的字段还有很多,更详细的可以查看博客https://blog.csdn.net/gatieme/article/details/51383272

二、编写内核模块打印task_struct重要字段

我们这里打印pcb的重要属性信息,比如状态信息,进程标识符,优先级信息,亲属关系,文件系统信息以及内存方面的信息。

task_struct.c:

#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>
 
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("begin...\n");
 
        //对链表遍历时,希望从第一个开始
        task=&init_task;  //指向0号进程pcb
 
        list_for_each(pos,&task->tasks) //遍历操作,使用pos指向,传入的参数task指向tasks字段.0号进程的tasks进行遍历。tasks将所有的进程连在一块。
        {
                p=list_entry(pos,struct task_struct,tasks);    //找到一个节点,就可以用这个节点的tasks字段,找到这个结构体的地址.对应的字段tasks
                //此时的p指针已经指向task_struct结构体的首部,后面就可以通过p指针进行操作
                count++;  //找到一个进程,自加
                printk("\n\n");
                printk("pid: %d; state: %d; prior: %d; static_pri: %d; parent_pid: %d; count: %d; umask: %d",p->pid,p->__state,p->prio,p->static_prio,(p->parent)->pid,atomic_read(&(p->files)->count),(p->fs)->umask);
        //linux中内核线程的mm是空的,要对它进行打印,就会出错,指针错误
        if((p->mm)!=NULL)
                printk("Total_vm: %ld",(p->mm)->total_vm);      //线性区总的页数
 
        }
 
        printk("进程的个数:%d\n",count);
 
        return 0;
}
 
static void __exit exit_pcb(void)    //出口函数
{
        printk("Exiting...\n");
}
 
// 指明入口点与出口点,入口/出口点是由module.h支持的
 
module_init(print_pcb);
module_exit(exit_pcb);

Makefile文件如下:

#Makefile文件注意:假如前面的.c文件起名为first.c,那么这里的Makefile文件中的.o文
#件就要起名为first.o    只有root用户才能加载和卸载模块
obj-m:=task_struct.o                          #产生task_struct模块的目标文件
#目标文件  文件  要与模块名字相同
CURRENT_PATH:=$(shell pwd)             #模块所在的当前路径
LINUX_KERNEL:=$(shell uname -r)        #linux内核代码的当前版本
LINUX_KERNEL_PATH:=/home/shupeiyao/linux-5.14.17

all:
	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules    #编译模块
#[Tab]              内核的路径       当前目录编译完放哪  表明编译的是内核模块

clean:
	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean      #清理模块

make编译

sudo insmod task_struct.ko 加载内核模块

dmesg 查看内核打印信息

在这里插入图片描述

有的没有打印total_vm,说明它的mm字段为空,是内核线程

在这里插入图片描述

线程共有361个

以上就是打印Linux内核中的task_struct结构中的重要字段的内容啦
如果对您有帮助麻烦点赞关注或者收藏哦~

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐