驱动模块(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) 收藏 举报