4.1 Linux内核模块简介
- 模块的特点:
- 模块本身不被编译入内核映像,从而控制了内核的大小。
- 模块一旦被加载,它就和内核中的其他部分完全一样。
- printk可定义输出级别
- lsmod命令可以获得系统中已加载的所有模块以及模块间的依赖关系,lsmod命令实际上是读取并分析“/proc/modules”文件,相当于 cat /proc/modules
- 内核中已加载模块的信息也存在于/sys/module目录下
- modprobe在加载某模块时,会同时加载该模块所依赖的其他模块。使用modprobe命令加载的模块若以“modprobe-r filename”的方式卸载,将同时卸载其依赖的模块。
- 模块之间的依赖关系存放在根文件系统的/lib/modules/<kernel-version>/modules.dep文件中,使用前需要depmod一下生成modules.dep文件
- 使用modinfo<模块名>命令可以获得模块的信息,包括模块作者、模块的说明、模块所支持的参数以及vermagic
4.2 Linux内核模块程序结构
- 模块加载函数:通过insmod或者modprobe时,模块加载函数会自动执行
- 模块卸载函数:通过rmmod时,模块卸载函数会自动执行。
- 模块许可声明:MODULE_LECENSE,可接受的LICENSE包括“GPL”、“GPL v2”、“GPL and additional rights”、“Dual BSD/GPL”、“Dual MPL/GPL”和“Proprietary”
- 模块参数、导出符号、作者信息
4.3 模块加载函数
- 内核模块加载函数一般以__init标识声明
static int __int hello_init(void)
{
...
return 0;
}
module_init(hello_init);
返回整型值,若初始化成功,应返回0。而在初始化失败时,应该返回错误编码。 - 在<linux/errno.h>中定义,包含-ENODEV、-ENOMEM之类的符号值。
- 可以使用request_module函数加载内核模块
eg:request_module(module_name); - 标识为__init的函数如果直接编译进入内核,成为内核镜像的一部分,在连接的时候都会放在.init.text这个区段内。在初始化完成后,释放init区段的内存。
- 数据也可以被定义为__initdata,对于只是初始化阶段需要的数据,内核在初始化完后,也可以释放它们占用的内存。
4.4 模块卸载函数
- 模块卸载函数以__exit标识声明,不返回任何值,用module_exit(函数名)指定
- 一般形式:
static void __exit hello_exit(void)
{
...
}
module_exit(hello_exit);
4.5 模块参数
- 模块传参:
- module_param();
- module_param_array();
- module_param_string();
- 用户向模块传参时,用” insmod 模块名 参数名=参数 ”
4.6 导出符号
- 可以通过宏定义进行Linux符号表导出:
- EXPORT_SYMBOL(符号名)
- EXPORT_SYMBOL_GPL(符号名)
- 使用时通过extern关键字声明
4.7 模块声明与描述
- 作者:MODULE_AUTHOR();
- 描述:MODULE_DECRIPTION();
- 版本:MODULE_VERSION();
- 设备表:MODULE_DEVICE_TABLE();
- 别名:MODULE_ALIAS();
4.8 模块使用计数
- 增加模块计数:int try_module_get();
- 减少模块计数:void module_put();
4.9 模块的编译