设计一个模块,列出所有内核线程的程序名、PID号和进程状态
设计一个模块,功能是列出系统中所有内核线程的程序名、PID号和进程状态。主要步骤:
- 阅读内核源代码,了解进程描述符task_struct中与本实验有关的成员项,以及访问进程队列的宏for_each_process;
- 分析内核模块实例,掌握内核模块的主要构成;
- 阅读Makefile文件,理解内核模块编译、加载过程;
实验过程
-
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 = NULL; printk(KERN_ALERT"名称\t 进程号\t 状态\t \r\n"); for_each_process(p) printk(KERN_ALERT "%d\t%d\t%d\n \r\n",p->comm,p->pid, p->state); return 0; } static void hello_exit(void) { printk(KERN_ALERT "hello world exit \r\n"); } module_init(hello_init);//加载函数 module_exit(hello_exit); //卸载函数 MODULE_LICENSE("GPL"); //许可证申明 MODULE_DESCRIPTION("list taskinfo module"); MODULE_AUTHOR("thirty14");
-
Makefile文件:
ifneq ($(KERNELRELEASE),) obj-m:=readprocess.o else KDIR:= /lib/modules/$(shell uname -r)/build PWD:= $(shell pwd) default: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: $(MAKE) -C $(KDIR) M=$(PWD) clean endif
-
验证一下
$ make # 编译 $ insmod hello.ko # 插入内科 $ dmesg # 查看内核 debug message
- 试验结束
实验参考:
一、Linux的内核模块
内核模块是Linux内核向外部提供的一个插口,其全称为动态可加载内核模块(Loadable Kernel Module,LKM),简称模块。Linux内核之所以提供模块机制,是因为它本身是一个单内核(monolithic kernel)。单内核的最大优点是效率高,因为所有的内容都集成在一起,但其缺点是可扩展性和可维护性相对较差,模块机制就是为了弥补这一缺陷。
模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。它在运行时被链接到内核作为内核的一部分在内核空间运行,这与运行在用户空间的进程是不同的。模块通常由一组函数和数据结构组成,用来实现一种文件系统、一个驱动程序或其他内核上层的功能。
总之,模块是一个为内核(从某种意义上来说,内核也是一个模块)或其他内核模块提供使用功能的代码块。
-
利用内核模块的动态装载性的优点
将内核映象的尺寸保持在最小,并具有最大的灵活性;便于检验新的内核代码,而不需重新编译内核并重新引导。 -
内核模块的缺点
装入的内核模块和其他内核部分一样,具有相同的访问权限,因此,差的内核模块会导致系统崩溃;为了使内核模块访问所有内核资源,内核必须维护符号表,并在装入和卸载模块时修改这些符号表;有些模块要求利用其他模块的功能,因此,内核要维护模块之间的依赖性。内核必须能够在卸载模块时通知模块,并且要释放分配给模块的内存和中断等资源;内核版本和模块版本的不兼容,也可能导致系统崩溃。
-
相关操作说明:
- 必需模块函数
module_init(hello_init); // 载入入口函数 module_exit(hello_exit); // 卸载模块退出函数
- 可选模块函数
MODULE_LICENSE(“*******”); // 许可证申明 MODULE_AUTHOR(“********”); // 作者申明 MODELE_DESCRIPTION(“***”); // 模块描述 MODULE_VERSION(“V1.0”); // 模块版本 MODULE_ALIAS("*********"); // 模块别名
- 加载内核模块
$ insmod hello.ko # 载入模块 $ rmmod hello # 卸载模块
- 查看加载模块是否成功
$ dmesg # 查看内核信息
- 必需模块函数
试验相关的内核代码
-
进程控制块的相关内容
// 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 */ }
-
for_each_process§ 宏
// 宏for_each_process(p)的功能是扫描整个进程链表 #define for_each_process(p) \ for (p = &init_task ; (p = next_task(p)) != &init_task ; ) // 使用方法 先定义一个task_struct结构的指针 struct task_struct *p; for_each_process(p){ // 使用for遍历链表,查找所有任务 }