驱动模块(7)——模块加载卸载与访问


1. try_module_get

try_module_get() 是一个关键的内核模块管理函数,主要用于动态增加模块的引用计数,确保模块在使用期间不会被意外卸载,从而维护系统的稳定性。

如果模块还在,则增加引用计数返回true表示可以访问模块提供的功能,若参数为NULL或模块正在被移除则返回false表示不应使用。

dma_buf_export
    if (!try_module_get(exp_info->owner))
        return ERR_PTR(-ENOENT);

(1) 典型应用场景:
当其他代码(如设备驱动、文件系统)需要调用模块提供的函数时,需通过 try_module_get() 增加其引用计数,防止模块在执行关键操作时被卸载。
a. 符设备驱动中,通过 file_operations 结构体的 .owner = THIS_MODULE 字段,内核在 open() 操作前自动调用 try_module_get();
b. 若直接调用模块导出函数(如符号表函数),需显式使用 try_module_get() 保护。

(2) 模块间依赖管理:
若模块 A 依赖模块 B 的功能,在调用 B 的接口前需增加 B 的引用计数,确保 B 未被卸载。

(3) 使用规范与注意事项:
a. 禁止在模块内部显式调用
try_module_get() 应在模块外部调用(由其他内核实体触发)。模块内部直接调用会导致时间窗口(time window)漏洞:模块可能已在卸载过程中,但尚未完成引用计数增加,引发内核崩溃.
b. 与 module_put() 配对使用。每次成功调用 try_module_get() 后,必须在操作结束时调用 module_put() 减少引用计数,否则模块将无法卸载。
c. 替代方案:owner 字段自动管理
在 file_operations、device_driver 等结构体中设置 .owner = THIS_MODULE,内核会自动在文件操作(如 open/read)前后调用 try_module_get()/module_put(),无需手动管理。####

(4) 常见错误与调试
a. 卸载失败(rmmod 报错)
若模块引用计数不为零,卸载时将提示 Module in use。可通过 lsmod | grep <模块名> 查看当前引用计数

# lsmod
Module                  Size  Used by
apu_top               196608  1 apusys

b. 强制卸载风险
使用 rmmod -f 可能跳过模块的 .exit 函数,导致资源泄漏或硬件状态异常,仅限调试场景
c. 日志分析
通过 dmesg 查看内核日志,定位未释放的引用(如 [ERROR] Module XXX has 2 lingering references)。

(5) 总结
try_module_get() 是 Linux 内核模块生命周期管理的核心机制之一,通过原子性地增加模块引用计数,确保模块在使用期间的稳定性。其正确使用需遵循以下原则:
a. 外部调用原则:由依赖模块的外部代码触发,而非模块自身((自身实现已经被使用了再加1已经来不及了,中间有个时间窗口可能已经卸载了)。
b. 配对操作:与 module_put() 严格配对,避免内存泄漏。
c. 优先自动管理:利用 owner = THIS_MODULE 自动处理引用计数(如设备驱动).

通过合理利用此机制,可显著提升内核模块的健壮性。更多实现细节可参考 Linux 内核源码(kernel/module.c)。

一个自己操作自己引用计数的不恰当的例子:

int open(struct inode *inode, struct file *filp) {
    if (!try_module_get(THIS_MODULE))
        return -ENODEV;
    ...
}

int release(...) {
    module_put(THIS_MODULE);
    ...
}

 


2. 获取实时模块状态

(1) /proc/modules

# cat /proc/modules
gps_pwr 24576 0 - Live 0x0000000000000000 (OE)

直接cat会模块名、大小、引用计数、模块状态。lsmod也有类似打印。

(2) /sys/module/XX

可通过 "/sys/module/<module-name>/initstate" 查看一个模块所处的状态,加载和卸载都如白驹过隙,所以绝大部分情况下看到的都是 "live"。


3. THIS_MODULE

当内核模块(.ko文件)被编译时,编译器会为每个模块生成一个名为 __this_module 的符号。该符号指向当前模块的 struct module 结构体实例。
模块的链接脚本(scripts/module.lds)会将 __this_module 放置在模块的特定段(如 .gnu.linkonce.this_module),确保其在加载时能被内核正确识别。

内核通过 load_module() 函数加载模块时,会解析模块的 ELF 格式,定位 __this_module 符号。然后将符号绑定到内核维护的模块链表(modules 全局变量)中的 struct module 实例。

__this_module 主要用于:
(1) 模块初始化与卸载:
在 module_init() 和 module_exit() 函数中,通过 THIS_MODULE 传递当前模块的上下文。
(2) 资源管理:
例如在 file_operations 结构中设置 owner = THIS_MODULE,确保模块在使用期间不会被卸载.

模块通过 EXPORT_SYMBOL 导出的函数会关联到 __this_module,内核借此跟踪模块间的依赖关系

关键机制总结对比:

-----------------------------------------------------------------------------------------------
机制            作用                                               实现位置
-----------------------------------------------------------------------------------------------
__this_module   指向当前模块的 struct module 实例                  编译器生成符号(链接脚本定位)
THIS_MODULE 宏    代码中安全引用 __this_module 的便捷方式            include/linux/module.h
struct module    描述模块状态、符号表、依赖关系的核心结构体         include/linux/module.h
load_module()    加载模块时解析 __this_module 并绑定到内核模块链表  kernel/module.c
-----------------------------------------------------------------------------------------------

 

 

 

 

参考:
使用 try_module_get 的正确姿势: https://zhuanlan.zhihu.com/p/603651555

 

posted on 2025-08-23 16:27  Hello-World3  阅读(32)  评论(0)    收藏  举报

导航