设计一个模块,功能是列出系统中所有内核线程的程序名、PID号和进程状态。主要步骤:
阅读内核源代码,了解进程描述符task_struct中与本实验有关的成员项,以及访问进程队列的宏for_each_process;
分析内核模块实例,掌握内核模块的主要构成;
阅读Makefile文件,理解内核模块编译、加载过程;
以下是hello.c文件
#include <linux/sched.h>
#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>
//#define for_each_process(p)
static int hello_init(void)
{
struct task_struct *p;
printk(KERN_ALERT"名称\t进程号\t状态\t");
// p=NULL;
for_each_process(p)
{
printk(KERN_ALERT"%d\t%d\t%d\n",p->comm,p->pid, p->state);
}
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "hello world exit\n");
}
module_init(hello_init);//加载函数
module_exit(hello_exit); //卸载函数
MODULE_LICENSE("GPL"); //许可证申明
MODULE_DESCRIPTION("hello module");
MODULE_AUTHOR("liguangcai");
以下是Makefile文件:
ifneq ($(KERNELRELEASE),)
obj-m:=readprocess.o
else
KDIR:= /lib/modules/$(shell uname -r)/build
PWD:= $(shell pwd)
default://注意,default前面最好不要有空格,刚开始做的时候前面加空格报错了
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:://注意,前面最好不要有空格
$(MAKE) -C $(KDIR) M=$(PWD) clean
endif://注意,前面最好不要有空格
编写完上述两个文件后回到这两个文件所在的目录键入命令: make

#insmod hello.ko
查看加载模块是否成功:
#dmesg

一、Linux的内核模块
内核模块是Linux内核向外部提供的一个插口,其全称为动态可加载内核模块(Loadable Kernel Module,LKM),简称模块。Linux内核之所以提供模块机制,是因为它本身是一个单内核(monolithic kernel)。单内核的最大优点是效率高,因为所有的内容都集成在一起,但其缺点是可扩展性和可维护性相对较差,模块机制就是为了弥补这一缺陷。
模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。它在运行时被链接到内核作为内核的一部分在内核空间运行,这与运行在用户空间的进程是不同的。模块通常由一组函数和数据结构组成,用来实现一种文件系统、一个驱动程序或其他内核上层的功能。
总之,模块是一个为内核(从某种意义上来说,内核也是一个模块)或其他内核模块提供使用功能的代码块。
1. 利用内核模块的动态装载性的优点:
将内核映象的尺寸保持在最小,并具有最大的灵活性;便于检验新的内核代码,而不需重新编译内核并重新引导。
2. 内核模块的缺点:
装入的内核模块和其他内核部分一样,具有相同的访问权限,因此,差的内核模块会导致系统崩溃;为了使内核模块访问所有内核资源,内核必须维护符号表,并在装入和卸载模块时修改这些符号表;有些模块要求利用其他模块的功能,因此,内核要维护模块之间的依赖性。内核必须能够在卸载模块时通知模块,并且要释放分配给模块的内存和中断等资源;内核版本和模块版本的不兼容,也可能导致系统崩溃。
相关操作说明:
a、必需模块函数
加载函数 module_init(hello_init);
卸载函数 module_exit(hello_exit);
b、可选模块函数
MODULE_LICENSE(“*******”); 许可证申明
MODULE_AUTHOR(“********”); 作者申明
MODELE_DESCRIPTION(“***”); 模块描述
MODULE_VERSION(“V1.0”); 模块版本
MODULE_ALIAS("*********"); 模块别名
c、加载内核模块
载入模块使用insmod命令:
#insmod hello.ko
卸载内核模块使用rmmod命令:
#rmmod hello
d、查看加载模块是否成功
dmesg可以看到系统的内核模块信息。
三、与本次试验相关的内核代码:
1. 进程控制块的相关内容:
linux/sched.h文件中:
#define TASK_RUNNING 0
#define TASK_INTERRUPTIBLE 1
#define TASK_UNINTERRUPTIBLE 2
#define __TASK_STOPPED 4
#define __TASK_TRACED 8
#define EXIT_ZOMBIE 16
#define EXIT_DEAD 32
#define TASK_DEAD 64
#define TASK_WAKEKILL 128
#define TASK_WAKING 256
#define TASK_PARKED 512
struct task_struct {
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
struct list_head tasks;
struct mm_struct *mm;
pid_t pid;
pid_t tgid;
struct task_struct __rcu *real_parent; /* real parent process */
struct task_struct __rcu *parent; /* recipient of SIGCHLD, wait4() reports */
char comm[TASK_COMM_LEN]; /* executable name excluding path */
}
2. for_each_process(p)宏:
宏for_each_process(p)的功能是扫描整个进程链表:
#define for_each_process(p) \ for (p = &init_task ; (p = next_task(p)) != &init_task ; )
使用方法:
struct task_struct *p;
for_each_process(p){
//对p指向的进程描述符进行操作
}