OS添加内核模块

一 题目介绍

(1)设计一个模块,要求列出系统中所有内核线程的程序名、PID、进程状态、进程优先级、父进程的PID。

(2)设计一个带参数的模块,其参数为某个进程的PID号,模块的功能是列出该进程的家族信息,包括父进程、兄弟进程和子进程的程序名、PID号、进程状态。

该题目要求学习Linux的模块化机制,学习通过模块编程的方式扩充Linux的功能,问题的关键在于掌握模块的基本概念和原理、掌握Linux系统的基本命令和使用方法,学习查看Linux系统进程的技术,掌握模块编程的能力。

二 实验思路

流程图

第一题中,题目要求遍历内核线程,输出它们的信息,因此,在init函数中,使用一个task_struct指针p获取进程信息即可,使用for_each_process宏遍历进程链表,使用p->mm判断该线程是否为进程线程,根据定义,当进程为内核线程时,mm等于NULL,当p遍历完后,运行exit清理函数,发送关闭信息。

流程图

第二题中,题目要求列出进程的家族信息,包括父进程、兄弟进程、子进程,因此仅需要一个信息量,即在module_param宏在init函数时传入pid号,在init函数定义task_struct指针用于遍历进程,使用list_for_each宏的p->parent->chilren遍历兄弟节点,由于该链表中,每个parent->chilren由sibling成员进行连接,因此在list_entry宏中使用sibling作为member量计算节点的首地址,存入psibling指针中,通过该指针打印出进程信息,遍历子进程时同理,list_for_each的宏参数改为p->chilren。

三 遇到问题及解决方法

  1. 实验(1)中的insmod操作时存在warning:module verification failed: signature and/or required key missing - tainting kerne,经查阅资料,发现是新版linux中出于安全考虑,需要自定义内核模块使用正确的数字签名,由于我编写的模块程序无签名,从而出现warning,但在目前的使用中可忽略。

  2. 实验(2)中对list_for_each和list_entry宏的使用原理不清楚,不理解为什么list_entry宏中member值需要使用sibling,经查询linux内核task_struct链表结构后明白,由于遍历兄弟进程时,需要从第一个兄弟进程开始即p->parent->chilren,而获得的该链表节点之间互为sibling,因此list_for_each得到的ptr指向了sibling节点头部,因此需要用sibling作为member值来计算task_struct的首地址,从而打印进程信息。

四 核心代码及实验结果展示

#PidsInfoModule.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
static int hello_init(void)

{

struct task_struct *p;

​ p = NULL;

​ p = &init_task;

​ printk(KERN_ALERT"名称\t进程号\t状态\t优先级\t父进程号\t");

​ for_each_process(p)

​ {

if(p->mm == NULL){

​ printk(KERN_ALERT"%s\t%d\t%d\t%d\n",p->comm,p->pid, p->__state,p->normal_prio,p->parent->pid);

​ }

​ }

return 0;

}

#makefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
obj-m:=PidsModule.o

PidsModule-objs:=PidsInfoModule.o

KDIR:= /lib/modules/$(shell uname -r)/build

PWD:= $(shell pwd)



default:

​ $(MAKE) -C $(KDIR) M=$(PWD) modules

clean:

​ $(MAKE) -C $(KDIR) M=$(PWD) clean

模块信息

dmesg信息打印

dmesg信息打印

#PidsFamily.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
static pid_t pid;

module_param(pid,int,0644);



static int hello_init(void)

{

struct task_struct *p;

struct list_head *pp;

struct task_struct *psibling;



p = pid_task(find_vpid(pid), PIDTYPE_PID);

printk("me: %s %d %d\n", p->comm, p->pid,p->__state);



if(p->parent == NULL) {

​ printk("No Parent\n");

}

else {

​ printk("Parent: %s %d %d\n", p->parent->comm, p->parent->pid, p->parent->__state);

}



list_for_each(pp, &p->parent->children)

{

​ psibling = list_entry(pp, struct task_struct, sibling);

​ printk("sibling %s %d %d\n", psibling->comm, psibling->pid,psibling->__state);

}



list_for_each(pp, &p->children)

{

​ psibling = list_entry(pp, struct task_struct, sibling);

​ printk("children %s %d %d\n", psibling->comm, psibling->pid,psibling->__state);

}



return 0;

}

#Makefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
obj-m:=Family.o

Family-objs:=PidsFamily.o

KDIR:= /lib/modules/$(shell uname -r)/build

PWD:= $(shell pwd)



default:

​ $(MAKE) -C $(KDIR) M=$(PWD) modules

clean:

​ $(MAKE) -C $(KDIR) M=$(PWD) clean

模块信息

pid=1时打印信息

pid=548时打印信息

五 个人实验改进与总结

5.1 个人实验改进

大部分代码参考书本实验指南进行编写,少部分不理解的代码上网查阅资料后编写,如

(1)我本来尝试每条打印信息均打印中文标识符,后参阅网上资料后,认为参考linux的ps命令的信息打印方法更加直观。

(2)中我本来打算使用书本中list_for_each_entry宏来进行遍历兄弟节点和子节点,但由于对task_struct结构的不熟悉,无法正确使用,在查阅资料后选择分别使用list_for_each和list_entry宏,使程序逻辑更加直观,但这样会显得代码没那么简洁。

(3)阅读书本及资料时,本打算使用task_struct.state输出进程状态,查阅相关资料后发现该成员随着版本更新需要改为__state。

5.2 个人实验总结

本次实验是我初步理解了task_struct进程标识符的结构,初步了解了linux内核中的链表原理,基础链表通过结合各种其他数据类型形成新的链表结构,从而形成高度抽象的linux数据结构,同时,也让我熟悉了基本内核编程的方法,包括init函数与exit函数的编写与调用、传入参数的方法、内核编译的方法以及查看内核运行信息的方法,同时也了解了linux进程之间的基本数据结构,学习了多种宏的使用,如list_entry等,学习了打印系统日志的方法和内核函数调用的方法,受益匪浅。

六 参考文献

  1. task_struct数据结构:http://blog.guorongfei.com/2015/02/26/lkd-chapter-three/

  2. mm的使用方法:https://cloud.tencent.com/developer/article/1368752

  3. parent/children/sibling之间的关系:https://www.796t.com/content/1544611525.html

  4. Task_struct state定义:https://blog.csdn.net/mybelief321/article/details/9048257


OS添加内核模块
http://example.com/2022/12/31/OS添加内核模块/
作者
Magnesium
发布于
2022年12月31日
许可协议