第四章Linux内核模块之八(模块的使用计数)

本文介绍Linux2.6及后续版本内核中模块使用计数的管理方式,包括try_module_get和module_put函数的作用及应用场景。当设备被使用时,通过增加相应模块的使用计数来确保管理模块不会被误卸载。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

4.8 模块的使用计数

Linux 2.4内核中,模块自身通过MOD_INC_USE_COUNT、MOD_DEC_USE_COUNT宏来管理自己被使用的计数。

Linux 2.6以后的内核提供了模块计数管理接口try_module_get(&module)和module_put(&module),从而取代Linux 2.4内核中的模块使用计数管理宏。模块的使用计数一般不必由模块自身管理,而且模块计数管理还考虑了SMP(对称多处理器)与PREEMPT(抢占)机制的影响。

int try_module_get(struct module *module);

该函数用于增加模块使用计数;若返回为0,表示调用失败,希望使用的模块没有被加载或正在被卸载中

void module_put(struct module *module);

该函数用于减少模块使用计数。

try_module_get()和module_put()的引入、使用与Linux 2.6以后的内核下的设备模型密切相关。Linux 2.6以后的内核为不同类型的设备定义了struct module*owner域,用来指向管理此设备的模块。当开始使用某个设备时,内核使用try_module_get(dev->owner)去增加管理此设备的owner模块的使用计数;当不再使用此设备时,内核使用module_put(dev->owner)减少对管理此设备的管理模块的使用计数。这样,当设备在使用时,管理此设备的模块将不能被卸载。只有当设备不再被使用时,模块才允许被卸载。

在Linux 2.6以后的内核下,对于设备驱动而言,很少需要亲自调用try_module_get()与module_put(),因为此时开发人员所写的驱动通常为支持某具体设备的管理模块,对此设备owner模块的计数管理由内核里更底层的代码(如总线驱动或是此类设备共用的核心模块)来实现,从而简化了设备驱动开发。

<think>我们正在使用C语言在Linux环境下检查某个内核模块是否已经加载。 方法:我们可以通过读取/proc/modules文件或使用syscall(如finit_module)来检查模块是否加载。但更简单的方法是解析/proc/modules,因为该文件列出了所有已加载的模块。 步骤: 1. 打开文件/proc/modules(或使用/proc/modules的符号链接/proc/modules -> self/modules)。 2. 读取文件内容,每一行代表一个已加载的模块。 3. 解析每一行,第一个字段就是模块名(直到第一个空格)。 4. 比较模块名是否与我们要查找的模块名相同。 注意:模块名可能会包含一些特殊字符,但通常不会包含空格(因为空格是分隔符)。所以我们可以安全地使用第一个空格前的字符串作为模块名。 另外,我们也可以使用函数get_kernel_module(),但这不是标准C库函数,因此我们选择读取/proc文件系统。 代码示例: 我们将编写一个函数,参数为要检查的模块名(字符串),返回值为整数:0表示已加载,1表示未加载,-1表示出错。 注意:由于/proc/modules的内容可能很大,我们逐行读取。 但是,请注意,模块名可能有版本后缀?实际上,在加载模块时,我们可以使用不带版本后缀的模块名,但在/proc/modules中,模块名后面可能会跟着一些数字(例如,如果多次加载同一个模块,会有一个计数,但通常不会)。所以我们应该只比较模块名部分(第一个字段)。 例如,/proc/modules中的一行可能是: nf_conntrack_netbios_ns 16384 0 - Live 0x0000000000000000 这里模块名是“nf_conntrack_netbios_ns”。 因此,我们只需要比较第一个字段。 实现: 我们将打开文件,逐行读取,然后分割出第一个字段(空格分隔),与目标模块名比较。 注意:模块名可能包含连字符、下划线等,所以直接字符串比较即可。 代码:</think>在 Linux 环境下,您可以使用 C 语言通过以下方法检测内核模块是否已加载: ### 方法 1:解析 `/proc/modules` 文件 Linux 内核通过 `/proc/modules` 虚拟文件公开所有已加载模块的信息。C 代码实现如下: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> int is_module_loaded(const char *module_name) { FILE *fp = fopen("/proc/modules", "r"); if (!fp) { perror("无法打开 /proc/modules"); return -1; // 错误 } char line[256]; int found = 0; while (fgets(line, sizeof(line), fp)) { // 提取模块名(行首第一个字段) char *name = strtok(line, " \t"); if (name && strcmp(name, module_name) == 0) { found = 1; break; } } fclose(fp); return found; } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "用法: %s <模块名>\n", argv[0]); return EXIT_FAILURE; } int loaded = is_module_loaded(argv[1]); if (loaded == 1) { printf("模块 '%s' 已加载\n", argv[1]); } else if (loaded == 0) { printf("模块 '%s' 未加载\n", argv[1]); } else { printf("检测失败\n"); } return EXIT_SUCCESS; } ``` ### 方法 2:使用 `sysfs` 接口 通过检查 `/sys/module/` 目录下的对应条目: ```c #include <sys/stat.h> #include <stdio.h> #include <stdlib.h> int is_module_loaded(const char *module_name) { char path[256]; snprintf(path, sizeof(path), "/sys/module/%s", module_name); struct stat st; if (stat(path, &st) == 0) { return S_ISDIR(st.st_mode); // 检查是否为目录 } return 0; } // main 函数同上 ``` ### 工作原理说明 1. **/proc/modules** 包含所有已加载模块的详细信息,格式为: `模块名 内存大小 引用计数 依赖列表 - 状态 地址` 例如:`ext4 657456 1 - Live 0xffffffffc0a00000` 2. **/sys/module/** 每个加载的模块会在此目录下创建同名子目录,包含模块的运行时参数和状态[^1]。 ### 使用示例 编译运行: ```bash gcc check_module.c -o check_module ./check_module ext4 ``` 输出: ``` 模块 'ext4' 已加载 ``` ### 注意事项 1. 模块名区分大小写 2. 需以 `root` 权限运行或用户有权限访问 `/proc` 和 `/sys` 3. 如果模块被内置到内核(非动态加载),上述方法可能检测不到
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值