目录
哪些常见的程序属于模块,模块的后缀名是什么?
Linux的驱动程序就是典型的模块程序,模块的后缀名为.ko
模块的入口函数通过带参数的宏module_init
来定义
模块的入口函数是指加载模块时会执行的函数:
比如下面这个代码:
module_init(led_init);
那么模块加载时会首先执行函数led_init
模块的出口函数通过通过带参数的宏module_exit
来定义
模块的出口函数是指卸载模块时会执行的函数。
比如下面这个代码:
module_exit(led_exit);
当模块卸载时会去执行函数led_exit
模块的源文件的末尾需利用宏MODULE_LICENSE
申明文件的许可证
典型的代码如下:
MODULE_LICENSE("GPL");
详细解释见
https://blog.csdn.net/wenhao_ir/article/details/144902881
模块如何加载和卸载
比如一个模块文件的名字为led_driver.ko
,那么运行下面的命令去加载它:
insmod led_driver.ko
那么运行下面的命令去卸载它
rmmod led_driver
注意:卸载模块时不需要加.ko
后缀
如何查看系统中已经加载的模块
用下面的命令:
lsmod
模块中的全局变量THIS_MODULE
,即__this_module
是什么东西?
__this_module
是一个由内核自动生成的全局变量,包含模块的相关信息,如模块名、引用计数等。
详细介绍见 https://blog.csdn.net/wenhao_ir/article/details/144906774
一个模块要调用另一个模块中的函数怎么办?
在 Linux 内核模块编程中,如果模块 A 中有一个函数 fun1
,并且模块 B 想要调用这个函数,仅仅加载 A 模块是不够的,模块 B 需要显式地声明并链接 A 模块中的 fun1
函数。以下是实现这一目标的步骤和原因:
1. 模块间的符号导出和引用
- 在 Linux 内核中,每个模块都有自己的符号表,函数和变量默认只在本模块内部可见。为了让其他模块访问模块 A 中的函数
fun1
,模块 A 需要将该函数导出为可供其他模块访问的符号。 - 导出符号:你需要在模块 A 中使用
EXPORT_SYMBOL()
或EXPORT_SYMBOL_GPL()
宏来导出fun1
函数,使其能够被其他模块(如模块 B)访问。例如:/* 在模块 A 中导出函数 */ int fun1(void) { /* 函数实现 */ } EXPORT_SYMBOL(fun1);
EXPORT_SYMBOL
将fun1
函数标记为导出符号,允许其他模块访问。
2. 在模块 B 中声明函数
- 即使模块 A 导出了
fun1
,模块 B 仍然需要显式地声明该函数,否则编译器无法识别该函数的存在。模块 B 必须通过头文件或直接声明来使用fun1
。 - 如果模块 A 提供了一个头文件(例如
a_module.h
),模块 B 可以包含该头文件,这样就能直接访问fun1
。例如:/* 在模块 B 中包含模块 A 的头文件 */ #include "a_module.h" // 假设 a_module.h 中声明了 fun1 void some_function(void) { fun1(); // 调用模块 A 中的 fun1 }
- 如果模块 A 没有提供头文件,模块 B 也可以直接在代码中声明
fun1
,例如:extern int fun1(void); // 声明模块 A 中的 fun1 void some_function(void) { fun1(); // 调用模块 A 中的 fun1 }
3. 加载模块 A 和模块 B
- 在模块 B 中正确声明并链接到模块 A 的函数后,你需要确保在加载模块 B 之前,模块 A 已经加载。模块 B 会在运行时动态解析
fun1
的符号,只要模块 A 已经加载并导出了符号,模块 B 就能够访问到fun1
。
4.小结
- 模块 A 必须显式导出
fun1
函数,使用EXPORT_SYMBOL
宏。 - 模块 B 需要声明并链接
fun1
,这可以通过包含模块 A 的头文件或在模块 B 中显式声明函数来完成。 - 仅仅加载模块 A 不足以让模块 B 调用
fun1
,模块 B 还需要正确地声明和链接这个函数。
模块要卸载时,如果之间存在依赖关系,怎么确定卸载顺序?
方法一就是如果你对这几个模块的源码足够熟悉,那你可以在脑子中分析出卸载顺序,但是智者千虑必有一失,何况很多时候你的源码中还用了第三方代码,那基本上就不可分析或者容易分析错误了…
最好的方式是利用命令
lsmod
查看模块的依赖情况。
比如现在我要卸载模块board_A_led、chip_demo_gpio、leddrv,我知道它们之间存在依赖关系,所以需要查看命令lsmod
查看下依赖关系,结果如下:
从中可以看到board_A_led是没有什么依赖情况存在的,它是独立的,所以它的卸载顺序任意。
但是leddrv
被chip_demo_gpio
使用着,即chip_demo_gpio
是依赖于leddrv
的。
所以对于chip_demo_gpio和leddrv,我们应该先卸载chip_demo_gpio
,再卸载leddrv
。
这里要提一个问题:假如两个模块互为依赖,怎么卸载?
答:在 Linux 内核中,两个模块互为依赖的情况很少见,因为这会导致你加载都无法正常加载…如果真出现了,最好的情况是重新设计代码。