一、linux内核模块简介
静态编译,把所需要的功能都编译到linux内核,会导致生成的内核会很大,如果我们要在现有的内核中新增或删除功能,将不得不重新编译内核。
动态编译,linux提供这样一种机制,称为模块(Module)。此机制的特点是,模块本身不被编译入内核映像,从而控制了内核的大小。内核一旦被加载,它就和内核中的其他部分完全一样。
在linux中,使用lsmod命令可以获得系统中加载了的所有模块以及模块间的依赖关系,lsmod命令实际上读取并分析"/proc/modules"文件。内核中已加载模块的信息也存在于/sys/module目录下。
modprobe命令比insmod命令要强大,它在加载某模块时,会同时加载该模块所依赖的其它模块。以modprobe -r filename的方式加载将同时卸载其依赖的模块。
二、linux内核模块程序结构
①模块加载函数(必须)
②模块卸载函数(必须)
③模块许可证声明(必须)
④模块参数(可选)
⑤模块导出符号(可选)
⑥模块作者等信息声明(可选)
三、模块加载函数
linux内核模块加载函数一般以__init标识声明,模块加载函数必须以module_init(函数名)的形式指定。返回整型值,初始化成功返回0。初始化失败时,应该返回错误编码,在linux内核里错误编码是一个负值。
四、模块卸载函数
linux内和模块卸载函数一般以__exit标识声明,模块卸载函数必须以module_exit(函数名)的形式指定,不返回任何值。
模块卸载函数完成与模块加载函数相反的功能:
模块加载函数注册了XXX,则模块卸载函数应注销XXX;
模块加载函数动态申请了内存,则模块卸载函数应释放该内存;
模块加载函数申请了硬件资源(中断、DMA通道、I/O端口和I/O内存等)的占用,则模块卸载函数应释放这些硬件资源。
模块加载函数开启了硬件,则卸载函数中一般要关闭。
五、模块参数
用“module_param(参数名,参数类型,参数读/写权限)”为模块定义一个参数,在装载内核模块时,用户可以向模块传递参数,形式为“insmod (或modprobe) 模块名 参数名=参数值”,如果不传递,参数使用模块内定义的缺省值。
六、导出符号
/proc/kallsyms文件对应着内核符号表,它记录了符号以及符号所在的内存地址。模块可以使用如下宏导出符号到内核符号表:
EXPORT_SYMBOL(符号名);
EXPORT _SYMBOL_GPL(符号名);
七、模块声明与描述
在linux内核模块中
MODULE_AUTHOR(author);声明模块的作者
MODULE_DESCRIPTION(description);声明模块描述
MODULE_VERSION(version_string);声明模块的版本
MODULE_DEVICE_TABLE(table_info);声明模块支持的设备表
MODULE_ALIAS(alternate_name);声明模块别名
八、模块的使用计数
try_module_get(&module) 该函数用于增加模块使用计数
module_put(&module) 该函数用于减少模块使用计数
九、模块的编译
Makefile文件应该与源码位于同一目录
#make modules
十、使用模块绕开GPL
①内核编译时选择“可以加载模块”
②将编译的.ko文件放置在目标文件系统的相关目录中
③文件系统应包含支持新内核的insmod、lsmod、rmmod等工具。
④可使用insmod命令手动加载模块
⑤修改启动过程的rc脚本(erc/init.d/rcS),增加insmod /.../xxx.ko