与Linux的模块(module-MODULE)相关的知识汇总

发布于:2025-02-10 ⋅ 阅读:(46) ⋅ 点赞:(0)

哪些常见的程序属于模块,模块的后缀名是什么?

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_SYMBOLfun1 函数标记为导出符号,允许其他模块访问。

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是没有什么依赖情况存在的,它是独立的,所以它的卸载顺序任意。
但是leddrvchip_demo_gpio使用着,即chip_demo_gpio是依赖于leddrv的。
所以对于chip_demo_gpio和leddrv,我们应该先卸载chip_demo_gpio,再卸载leddrv

这里要提一个问题:假如两个模块互为依赖,怎么卸载?
答:在 Linux 内核中,两个模块互为依赖的情况很少见,因为这会导致你加载都无法正常加载…如果真出现了,最好的情况是重新设计代码。

未完待续…


网站公告

今日签到

点亮在社区的每一天
去签到