终于来到模块编译,历经“九九八十一难”,终于取到了经。哈哈,废话不多说,进入正题。
先来看下概念吧:Linux内核是单体式结构,相对于微内核结构而言,其运行效率高,但系统的可维护性及可扩展性较差。
为此,linux提供了内核模块(module)机制,它不仅可以弥补单体式内核相对于微内核的一些不足,而且对系统性能没有影响。内核模块的全称是动态可加载内核模块(Loadable Kernel Module,KLM),简称为模块。模块是一个目标文件,能完成某种独立的功能,但其自身不是一个独立的进程,不能单独运行,可以动态载入内核,使其成为内核代码的一部分,与其他内核代码的地位完全相同。当不需要某模块功能时,可以动态卸载。实际上,linux中大多数设备驱动程序或文件系统都以模块方式实现,因为它们数目繁多,体积庞大,不适合直接编译在内核中,而是通过模块机制,需要时临时加载。使用模块机的另一个好处是,修改模块代码后只需重新编译和加载模块,不必重新编译内核和引导系统,降低了系统功能的更新难度。 一个模块通常由一组函数和数据结构组成,用来实现某种功能,如实现一种文件系统、一个驱动程序或其他内核上层的功能。模块自身不是一个独立的进程,当前进程运行是调用到模块代码时,可以认为该段代码就代表当前进程在核心态运行。
可以把内核看成一台电脑哈,应用程序就相当于这些模块了,当你想使用某些功能的“程序”时,
你只需要安装相应的“程序”,不想使用了,可以把它卸载掉。
不管是“程序”安装还是“卸载”都不会影响到当前的“电脑”(不需要关机重启等),即热插拔式。linux 内核模块就是这样,你只要把写好的模块编译完成,就可以动态加载(热插拔)了。
笔者为了做这个试验,可谓是含辛茹苦(老师给的资料有问题,Makefile 文件(后面会提到)的编写,建议不要看老师的资料)刚开始参考老师的资料,连个helloworld 模块都跑不通,好扎心...在百度的帮助下,终于
看到了 hello wolrd(黑暗中迎来一丝曙光)沉重的心终于可以放一放了。
该模块在加载到内核时会调用 hello_init(void) 函数,在卸载该模块时会调用hello_exit()函数,,printk()函数会把内容写入到系统之日中,使用dmesg命令即可查看
在linux模块编程中,必须要有两个函数,一个用来初始化模块,另一个用来对资源进行释放和清理。linux使用module_init 和 modlue_exit来注册这两个函数。MODULE_LICENSE("GPL")表示模块许可申明的。只有使用MODLUE_LICENSE宏申明的模块,才会被linux内核接受,否则会收到“kernel tainted”警告(好像也没啥的,一个警告而已,大家可以试试)
下面是之前提到Makefile文件,用来配置模块编译的
这里我的模块源码是hello.c obj-m +=hello.o 这里只要写成hello.o(响应模块源码的名字)
LINUX_KERNEL_PATH 一定要写成你源码所在的目录,否则编译出错的。
下面的内容,相信大家非常喜欢(废话说了一大堆),真材实料来了:
设计一个模块,要求列出系统中所有内核线程的程序名、PID号、进程状态及进程优先级。
直接贴源码了
注意#include<linux/init_task.h> 不是#include<linux/init.h>哦 笔者在这个坑踩了好久(都快绝望了)
还好今天浏览网页
笔者之前一直使用#include<linux/init.h>这个头文件,在make的时候总是会包 init_task undelcared,
看到了这个网页,我就把头文件换成了init_task.h 没想到编译成功了。
那么insmod info.koin一下吧 使用dmesg查看
成功输出
设计一个带参数的模块,其参数为某个进程的PID号,该模块的功能是列出该进程的家族信息,包括父进程、兄弟进程和子进程的程序名、PID号。
详细内容不在介绍了,这里已经没有坑了
直接贴源码
记得修改Makefile文件
make
这里我打开火狐浏览器
使用top命令查看到该进程的pid是3016
接下来 在终端里输入 insmod infoByPid.ko pid=3016,这里pid=3016 是源码的里的pid这个全局变量
可以看到结果
好了,完成,收工。