一:Linux内核模块组成部分
1.linux内核模块加载
2.linux内核模块卸载
3.linux内核许可证声明(GPL,GPLv2)
4.Linux内核模块参数
5.Linux内核模块导出符号
6.linux作者声明等信息
1.Linux内核模块加载
(1)加载模块命令:(用户空间Linux内核模块的加载)
insmod $module_name
(2)模块加载的时候最终调用到模块初始化函数
static init __init xxx_init(){
/*code*/
}
module_init(xxx_init);
__init为模块函数定义的修饰符
module_init(init_fuc)为模块函数胡初始化注册调用。
(3)内核模块的加载可以用
request_module(module_name)
(4)__init标识符
#define __init __atrribute__((__section__(".init.text")))
如何模块函数编译进内核,.init.text区域保留这__init模块初始化函数的地址指针,在内核初始化的时候会调用这些函数进行模块的加载初始化。初始化完成后进行释放init区域的内存。
(5)__initdata
```cpp
static init m_init_data __initdata = 1;
initdata跟init函数类似,只不过__initdata的数据会放在init.data
**
2.linux内核卸载模块
**
(1)卸载命令rmmod
insmod $module_name
(2)模块卸载函数
static void __exit exit_func()
{
/*code*/
}
module_exit(exit_func);
__exit为模块卸载函数的标示符
module_exit相当于注册模块卸载函数的注册函数
内核模块卸载函数无返回值
注意:一般来说当模块编译进内核胡时候模块内核函数是不会链接进内核的,因为本身就在内核里面了,卸载它根本不可行。
(4)__exitdata数据
在内核退出时候一些内核数据初始化
总结1:
总的来说,Linux模块加载以及卸载的设计的思想很简单,那就是模块化管理,在新加内核模块的时候只需要完成两件事情:
(1)内核模块加载函数(提供module_init()注册模块函数的接口以及模块函数的规范)
(2)内核模块写卸载函数(提供module_exit()注册模块函数的接口以及模块函数的规范)
3.内核模块参数
模块参数可以是内核提供给外部一个变量参数,可以用户通过模块参数来进行一些内核模块一些设置。
一般的模块参数定义:
(1)module_param(参数名,参数类型,参数读写权限);
static char *my_char = "my_module_param";
module_param(my_name,charp,S_IRUGO);//S_IRUGO在src/include/stat.h中声明
static int my_num = 1000;//初始化一个内核模块参数
module_param(my_num,int,S_IRUGO);//向内核注册模块参数
(2)数组作为模块参数
module_param_array(数组名,数组类型,数组大小,数组读写权限);
(3)用户空间初始化模块参数
在用户空间加载内核模块时候可以初始化模块参数
insmod $module_name module_param_name = module_param_value//一般模块参数的初始化
Insmod $module_name module_param_array_name = a0,a1,a2.... //数组模块参数的初始化,a0,a1....数组从0往后依次赋值
(4)uboot传递的时候对模块参数复制
module_name.module_param = module_param_value //在uboot引导以及初始化内核的时候自动会对这些参数进行解析,在android平台中很多soc的方案商都会利用Lk给内核模块参数初始化,特别是多屏兼容的时候
(5)模块参数的查看
一般来说当模块加载后,只要访问权限(读写),都可以在用户空间下访问,访问目录一般在/sys/module/模块名字/parameters目录下。如下deepin系统下的ext4模块参数。
entle@entle-PC:/sys/module/ext4/parameters$ ls
userns_mounts
entle@entle-PC:/sys/module/ext4/parameters$ cat userns_mounts
N
总结:
模块参数其实就是给内核提供给外部的一个变量参数控制,方便一些模块动态设置。
4.内核模块导出符号
(1)作用:主要是为了内核模块间可以相互访问变量以及函数
(2)实现:/proc/kallsyms 文件中对应着内核符号表,记录了符号以及符号所在的内存地址
entle@entle-PC:/$ cat /proc/kallsyms
0000000000000000 T mei_cl_bus_init [mei]
0000000000000000 T mei_cl_bus_init [mei]
0000000000000000 T mac_hid_init [mac_hid]
0000000000000000 T dw_init [dw_dmac]
0000000000000000 T acpi_pad_init [acpi_pad]
0000000000000000 T parport_parse_param.constprop.21 [parport_pc]
0000000000000000 T parport_parse_param.constprop.21 [parport_pc]
0000000000000000 T ppdev_init [ppdev]
0000000000000000 T lp_init_module [lp]
0000000000000000 T parport_default_proc_register [parport]
0000000000000000 T read_file_content [vfs_monitor]
0000000000000000 T read_file_content [vfs_monitor]
0000000000000000 T parse_mounts_info [vfs_monitor]
0000000000000000 T init_module [vfs_monitor]
(3)导出符号表的宏
EXPORT_SYMBOL(符号名)//
EXPORT_SYMBOL_GPL(符号名)//适用于GPL许可权的内核模块
(4)实现步骤,B调用A的函数或者变量
A里面导出符号表:EXPORT_SYMBOL。
B的程序里面使用extern声明该变量或函数。
B的Makefile里面加入:KBUILD_EXTRA_SYMBOLS = /export_symbol_module_dir/Module.symvers
B和A相互调用。。。。。
5.模块信息描述
MODULE_AUTHOR(author);//模块作者
MODULE_DESCRIPTION(description);//模块描述
MODULE_VERSION(version_string);//模块版本
MODULE_DEVICE_TABLE(table_info);//模块设备表,usbs通常用此表示usb支持的usb设备
MODULE_ALIAS(alternatename);//模块别名
6.模块的使用计数
int try_module_get(struct module *module);//模块使用增加计数
void module_put(struct module *module);//减少模块使用计数
总的来说,内核模块化的设计使得内核的模块化管理更加规范以及使Linux驱动开发者不用关心
内核的核心构成以及运作,只需要根据内核提供内核模块加载以及卸载胡相关接口进行开发便可,这给开发者带来了便捷。对程序的移植也有很大的帮助。