本节主要编写一个内核模块,实现进程PID和名称的打印。这里,我们需要了解linux内核中,链表的实现、PCB(进程控制块)的定义。编程不是目的,目的是更加了解内核中相关代码的实现。至于如何编写和运行一个模块,请参考:
在Ubuntu 18.04环境下编写一个简单的内核模块
遍历进程的模块源码
# include
# include
# include
# include
# include
static int print_pid(void)
{
struct task_struct * task, * p;
struct list_head * pos;
int count = 0;
printk("Hello World enter begin:
");
task =& init_task;
list_for_each(pos, &task->tasks)
{
p = list_entry(pos, struct task_struct, tasks);
count++;
printk("%d---------->%s
", p->pid, p->comm);
}
printk("the number of process is: %d
", count);
return 0;
}
static int __init lkp_init(void)
{
printk("<1>Hello, World! from the kernel space...
");
print_pid;
return 0;
}
static void __exit lkp_cleanup(void)
{
printk("<1>Good Bye, World! leaving kernel space..
");
}
module_init(lkp_init);
module_exit(lkp_cleanup);
MODULE_LICENSE("GPL");
print_pid() 用来打印进程的PID和进程名,有同学可能对上一段代码不太了解,所以在这里,先抛出代码的作用就在于,可以根据不懂的地方,查漏补缺。
1.内核中链表的定义
我们知道,以双向链表作为基本的数据结构,可以演化出其他复杂的数据结构,如栈、队列等。Linux内核中,也是如此,当然,随着内核不断的优化,如今的内核版本,在设计链表时,处处体现其精妙绝伦的设计思想。
struct list_head
{
struct list_head *next, *prev;
}
这是内核中链表的一段源码,整个链表的位置在ubuntu中如下所示:
对于内核中的链表,我们不对整个list.h进行分析,只是大体介绍一下linux链表设计的思想。
在内核中,上述结构体是一个不含数据域的链表,可以嵌入任何结构中,实现各种链表或者其他的数据结构。比如,下面的代码就嵌入了之前的内核链表,从而实现一个双向链表:
struct test_list
{
void * my_data;
struct list_head list;
}
在一个结构体中嵌套另外一个结构体,可以把它想象成递归。对于list.h的详细分析,会在以后给与详细的解释。
2.进程控制块(PCB)
什么是PCB?
操作系统为了对进程进行管理,势必需要对进程生命周期中的每一个阶段进行详细描述,linux内核中就用一个统一的数据结构来描述进程的状态,这样的数据结构我们称之为进程控制块PCB(Process Control Block)
内核中的PCB是一个相当庞大的结构体,主要包含状态信息、链接信息、各种标识符、进程间通信、时间和定时器信息、调度信息、文件系统信息、虚拟内存信息和处理器环境等。其在内核中的位置:
关于详细的PCB分析,日后再进行介绍。
3.进程链表
为了对给定类型的进程进行有效搜索,内核建立了几个进程链表。每个链表由指向进程PCB的指针组成。在sched.h文件的task_struct结构体中,有如下代码,实现进程链表。
struct task_struct
{
struct list_head tasks;
char comm[TASK_COMM_LEN];
}
其实就是通过双向循环链表把所有进程联系起来