LV10-01-内核模块-02-模块参数与依赖
本文主要是内核模块的参数与依赖相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
Windows | windows11 |
Ubuntu | Ubuntu16.04的64位版本 |
VMware® Workstation 16 Pro | 16.2.3 build-19376536 |
SecureCRT | Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日 |
Linux开发板 | 华清远见 底板: FS4412_DEV_V5 核心板: FS4412 V2 |
u-boot | 2013.01 |
点击查看本文参考资料
参考方向 | 参考原文 |
--- | --- |
点击查看相关文件下载
文件 | 下载链接 |
--- | --- |
一、模块传参
还记得main
函数有两个参数吗,我们在执行可执行文件的时候,可以直接在命令行向可执行程序传递参数,那模块呢?是没有main
函数的,我们动态加载模块的时候想要给它传递参数怎么办呢?
1. module_param()
1.1 函数说明
我们可以通过以下命令来查看在linux
源码中含有该函数的文件:
1 | grep module_param -r -n ~/5linux/linux-3.14include/ |
经过查找,我们可以得到如下信息:
1 | /* 需包含的头文件 */ |
【函数说明】该函数,严格来说应该是一个宏,它将指定的全局变量设置成模块参数。
【函数参数】
name
:全局变量名。type
:变量类型,c
中的变量类型与.ko
模块函数使用的类型符号对应关系如下:
点击查看对应关系
函数使用符号 | 实际类型 | 传参方式 |
bool | bool | insmod xxx.ko 变量名=0 或 1 |
invbool | bool | insmod xxx.ko 变量名=0 或 1 |
charp | char * | insmod xxx.ko 变量名="字符串内容" |
short | short | insmod xxx.ko 变量名=数值 |
int | int | insmod xxx.ko 变量名=数值 |
long | long | insmod xxx.ko 变量名=数值 |
ushort | unsigned short | insmod xxx.ko 变量名=数值 |
uint | unsigned int | insmod xxx.ko 变量名=数值 |
ulong | unsigned long | insmod xxx.ko 变量名=数值 |
perm
:给对应文件/sys/module/name/parameters/变量名
指定操作权限。
模块插入成功后,模块的变量都会在/sys/module/name/parameters/
目录下生成一个以变量名为名称的文件,我们使用模块参数的话,需要给这些文件指定一个权限,我们一般会设置为0664
。
【返回值】none
。
【使用格式】一般情况下基本使用格式如下:
1 | /* 需要包含的头文件 */ |
【注意事项】none
1.2 使用实例
暂无
2. module_param_array()
2.1 函数说明
我们可以通过以下命令来查看在linux
源码中含有该函数的文件:
1 | grep module_param_array -r -n ~/5linux/linux-3.14include/ |
经过查找,我们可以得到如下信息:
1 | /* 需包含的头文件 */ |
【函数说明】该函数,严格来说应该是一个宏,它将指定的全局变量设置成模块参数。
【函数参数】
name
:全局变量名。type
:指数组中元素的类型,c
中的变量类型与.ko
模块函数使用的类型符号对应关系如下:
点击查看对应关系
使用符号 | 实际类型 | 传参方式 |
bool | bool | 数组名=元素值0,元素值1,...元素值num-1 |
invbool | bool | |
charp | char * | |
short | short | |
int | int | |
long | long | |
ushort | unsigned short | |
uint | unsigned int | |
ulong | unsigned long |
&num
:存放数组大小变量的地址,可以填NULL
(确保传参个数不越界)。perm
:给对应文件/sys/module/name/parameters/变量名
指定操作权限。
模块插入成功后,模块的变量都会在/sys/module/name/parameters/
目录下生成一个以变量名为名称的文件,我们使用模块参数的话,需要给这些文件指定一个权限,我们一般会设置为0664
。
【返回值】none
。
【使用格式】一般情况下基本使用格式如下:
1 | /* 需要包含的头文件 */ |
【注意事项】none
3. MODULE_PARAM_DESC
我们可以使用MODULE_PARAM_DESC
宏对每个参数进行作用描述,一般格式如下:
1 | MODULE_PARM_DESC(变量名,字符串常量); |
其中字符串常量的内容用来描述对应参数的作用,查看的时候可以用以下命令查看:
1 | modinfo module_name.ko |
4. 使用实例
点击查看实例
1 |
|
动态加载的时候使用以下命令加载:
1 | insmod <module_name>.ko gx=20 gstr="qidaink" garr=6,7,8,9,10 |
二、模块依赖
内核模块的代码与其它内核代码共用统一的运行环境,也就是说模块只是存在形式上独立,运行上其实和内核其它源码是一个整体,它们隶属于同一个程序,因此一个模块或内核其它部分源码应该可以使用另一个模块的一些全局特性。
1. 导出符号
一个模块中这些可以被其它地方使用的名称被称为导出符号,所有导出符号被填在同一个表中这个表被称为符号表。最常用的可导出全局特性为全局变量和函数 。
2. 查看符号表
我们可以通过nm
命令查看elf
格式的可执行文件或目标文件中包含的符号表,一般格式如下:
1 | nm 文件名 |
我们可以使用man nm
命令来查看符号表中各个符号前边的字母的含义。
3. 符号导出与使用
我们在模块B
中要用到模块A
中的全局变量的话,需要这样来操作:
- 模块
A
中:导出模块B
所需要的全局变量符号;
1 | // 模块A |
- 模块
B
中:先对模块A
导出的符号进行extern
声明,然后才可以使用。
1 | // 模块B |
Linux
为我们提供了两个宏,用于导出全局变量符号:
1 | EXPORT_SYMBOL(函数名或全局变量名); |
4. 依赖关系
像上边所说,B
模块使用了A
模块导出的符号,此时称B
模块依赖于A
模块,则有:
- 编译次序
当两个模块源码在相同目录时:
(1)先编译需要导出符号的模块A
;
(2)再编译使用符号的模块B
.
当两个模块源码在不同目录时:
(1)先编译需要导出符号的模块A
;
(2)拷贝A
模块目录中生成的Module.symvers
到B
模块目录(否则编译B
模块时会有符号未定义错误);
(3)最后编译使用符号的模块B
。
- 加载次序
先插入提供导出符号的A
模块,再插入使用符号的B
模块,否则B
模块插入会失败。
1 | insmod A.ko # 先加载 |
- 卸载次序
先卸载使用符号的B
模块,再卸载提供导出符号的A
模块,否则A
模块卸载会失败。
1 | rmmod B.ko # 先卸载 |