query_module 函数详解
1. 函数介绍
query_module
是 Linux 系统中用于查询内核模块信息的系统调用。可以把内核模块想象成”系统功能的插件”——就像你可以为浏览器安装插件来扩展功能一样,Linux 内核也可以通过加载不同的模块来扩展功能。
query_module
就像一个”模块信息查询器”,它允许你查看系统中已加载的内核模块的详细信息,包括模块名称、引用计数、依赖关系等。
2. 函数原型
#include <linux/module.h>
int query_module(const char *name, int which, void *buf,
size_t bufsize, size_t *ret);
3. 功能
query_module
函数用于查询内核模块的各种信息。它可以获取模块列表、模块引用计数、模块符号信息、模块依赖关系等。
4. 参数
- name: 要查询的模块名称(NULL 表示查询所有模块)
- which: 查询类型(指定要获取的信息类型)
- buf: 指向缓冲区的指针,用于存储返回的信息
- bufsize: 缓冲区大小
- ret: 指向返回值的指针
5. 查询类型(which 参数)
类型 | 值 | 说明 |
---|---|---|
QM_MODULES | 1 | 获取模块名称列表 |
QM_REFS | 2 | 获取模块引用计数 |
QM_SYMBOLS | 3 | 获取模块符号信息 |
QM_INFO | 4 | 获取模块详细信息 |
QM_MODINFO | 5 | 获取模块信息(新版本) |
6. module_info 结构体
struct module_info {
unsigned long addr; /* 模块地址 */
unsigned long size; /* 模块大小 */
unsigned long flags; /* 模块标志 */
long refcnt; /* 引用计数 */
unsigned char usecount; /* 使用计数 */
};
7. 返回值
- 成功: 返回 0
- 失败: 返回 -1,并设置相应的 errno 错误码
8. 常见错误码
EPERM
: 权限不足(通常需要 root 权限)EINVAL
: 参数无效ENOENT
: 指定的模块不存在ENOMEM
: 内存不足EFAULT
: 参数指针无效ENOSYS
: 系统不支持此调用
9. 相似函数或关联函数
- lsmod: 命令行工具列出模块
- insmod: 加载内核模块
- rmmod: 卸载内核模块
- modprobe: 智能加载内核模块
- /proc/modules: 模块信息文件
- /sys/module/: 模块 sysfs 接口
- kmod: 内核模块管理工具
10. 示例代码
示例1:基础用法 – 查询模块列表
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/module.h>
#include <errno.h>
#include <string.h>
// 注意:query_module 在现代 Linux 系统中可能不可用
// 这里提供概念性示例和现代替代方案
int main() {
printf("=== query_module 基础示例 ===\n\n");
printf("注意: query_module 是已废弃的系统调用\n");
printf("在现代 Linux 系统中通常不可用\n\n");
printf("功能说明:\n");
printf("query_module 用于查询内核模块信息:\n");
printf("1. QM_MODULES: 获取模块名称列表\n");
printf("2. QM_REFS: 获取模块引用计数\n");
printf("3. QM_SYMBOLS: 获取模块符号信息\n");
printf("4. QM_INFO: 获取模块详细信息\n");
printf("5. QM_MODINFO: 获取模块信息(新版本)\n");
printf("\n=== 现代替代方案 ===\n");
printf("1. 使用 /proc/modules 文件\n");
printf("2. 使用 lsmod 命令\n");
printf("3. 使用 modinfo 命令\n");
printf("4. 使用 /sys/module/ 接口\n");
return 0;
}
示例2:现代替代方案 – 模块信息查询
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
// 通过 /proc/modules 获取模块信息
int get_modules_via_proc() {
printf("=== 通过 /proc/modules 获取模块信息 ===\n");
FILE *fp = fopen("/proc/modules", "r");
if (!fp) {
perror("打开 /proc/modules 失败");
return -1;
}
printf("%-25s %-10s %-8s %-15s %s\n",
"模块名称", "大小", "使用数", "被谁使用", "状态");
printf("%-25s %-10s %-8s %-15s %s\n",
"--------", "----", "----", "------", "----");
char line[512];
int count = 0;
while (fgets(line, sizeof(line), fp) && count < 20) {
char module_name[64];
unsigned long size;
int use_count;
char used_by[256];
char status[32];
char address[32];
// 解析 /proc/modules 格式
int parsed = sscanf(line, "%63s %lu %d %255s %31s %31s",
module_name, &size, &use_count, used_by, status, address);
if (parsed >= 4) {
printf("%-25s %-10lu %-8d %-15s %s\n",
module_name, size, use_count, used_by,
parsed >= 5 ? status : "-");
count++;
}
}
fclose(fp);
if (count == 0) {
printf("没有模块信息\n");
} else {
printf("\n显示了前 %d 个模块\n", count);
}
return 0;
}
// 通过 /sys/module/ 获取模块详细信息
int get_module_details_via_sysfs() {
printf("\n=== 通过 /sys/module/ 获取模块详细信息 ===\n");
// 检查 /sys/module/ 目录
if (access("/sys/module", F_OK) != 0) {
printf("系统不支持 /sys/module/ 接口\n");
return -1;
}
// 列出几个模块的详细信息
const char *modules[] = {"usbcore", "tcp", "ext4", "loop", NULL};
for (int i = 0; modules[i]; i++) {
char module_path[256];
snprintf(module_path, sizeof(module_path), "/sys/module/%s", modules[i]);
if (access(module_path, F_OK) == 0) {
printf("\n模块: %s\n", modules[i]);
// 检查模块参数
char param_path[256];
snprintf(param_path, sizeof(param_path), "%s/parameters", module_path);
if (access(param_path, F_OK) == 0) {
printf(" ✓ 支持参数配置\n");
}
// 检查模块引用
char refcnt_path[256];
snprintf(refcnt_path, sizeof(refcnt_path), "%s/refcnt", module_path);
FILE *refcnt_fp = fopen(refcnt_path, "r");
if (refcnt_fp) {
char refcnt[32];
if (fgets(refcnt, sizeof(refcnt), refcnt_fp)) {
printf(" 引用计数: %s", refcnt);
}
fclose(refcnt_fp);
}
// 检查模块状态
char initstate_path[256];
snprintf(initstate_path, sizeof(initstate_path), "%s/initstate", module_path);
FILE *initstate_fp = fopen(initstate_path, "r");
if (initstate_fp) {
char initstate[32];
if (fgets(initstate, sizeof(initstate), initstate_fp)) {
printf(" 初始化状态: %s", initstate);
}
fclose(initstate_fp);
}
}
}
return 0;
}
// 模拟 query_module 功能
void simulate_query_module() {
printf("\n=== 模拟 query_module 功能 ===\n");
printf("QM_MODULES (获取模块列表):\n");
printf(" 模块1: usbcore\n");
printf(" 模块2: tcp\n");
printf(" 模块3: ext4\n");
printf(" 模块4: loop\n");
printf(" 模块5: sd_mod\n");
printf("\nQM_INFO (获取模块详细信息):\n");
printf(" 模块名称: usbcore\n");
printf(" 地址: 0xffffffffc0000000\n");
printf(" 大小: 123456 字节\n");
printf(" 引用计数: 5\n");
printf(" 状态: Live\n");
printf("\nQM_REFS (获取引用计数):\n");
printf(" usbcore: 5\n");
printf(" tcp: 3\n");
printf(" ext4: 2\n");
printf(" loop: 1\n");
printf("\nQM_SYMBOLS (获取符号信息):\n");
printf(" usb_register: 0xffffffffc0001000\n");
printf(" usb_deregister: 0xffffffffc0002000\n");
printf(" usb_submit_urb: 0xffffffffc0003000\n");
}
int main() {
printf("=== 内核模块查询系统 ===\n\n");
// 显示系统信息
printf("系统信息:\n");
printf(" 用户 ID: %d\n", getuid());
printf(" 进程 ID: %d\n", getpid());
// 检查权限
if (getuid() != 0) {
printf(" 注意: 某些模块信息需要 root 权限\n");
}
printf("\n");
// 通过 /proc/modules 获取信息
get_modules_via_proc();
// 通过 /sys/module/ 获取详细信息
get_module_details_via_sysfs();
// 模拟 query_module 功能
simulate_query_module();
printf("\n=== 现代模块管理工具 ===\n");
printf("常用的模块管理命令:\n");
printf("1. lsmod # 列出已加载模块\n");
printf("2. modinfo module # 显示模块信息\n");
printf("3. insmod module.ko # 加载模块\n");
printf("4. rmmod module # 卸载模块\n");
printf("5. modprobe module # 智能加载模块\n");
printf("6. depmod # 生成模块依赖\n");
printf("\n");
printf("模块信息文件:\n");
printf("1. /proc/modules # 已加载模块列表\n");
printf("2. /proc/devices # 设备号信息\n");
printf("3. /sys/module/ # 模块 sysfs 接口\n");
printf("4. /lib/modules/ # 模块文件目录\n");
return 0;
}
示例3:完整的模块管理工具
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
// 配置结构体
struct module_config {
int list_modules; // 列出模块
int show_details; // 显示详细信息
int verbose; // 详细输出
int show_refs; // 显示引用计数
int show_symbols; // 显示符号信息
char *module_name; // 指定模块名称
char *search_pattern; // 搜索模式
};
// 模块信息结构体
struct module_info {
char name[256];
unsigned long size;
int ref_count;
char used_by[512];
char status[32];
unsigned long address;
};
// 通过 /proc/modules 解析模块信息
int parse_proc_modules(struct module_info *modules, int max_modules) {
FILE *fp = fopen("/proc/modules", "r");
if (!fp) {
return -1;
}
int count = 0;
char line[1024];
while (fgets(line, sizeof(line), fp) && count < max_modules) {
struct module_info *mod = &modules[count];
char extra[512];
// 解析 /proc/modules 格式
int parsed = sscanf(line, "%255s %lu %d %511s %31s %31s",
mod->name, &mod->size, &mod->ref_count,
mod->used_by, mod->status, extra);
if (parsed >= 4) {
count++;
}
}
fclose(fp);
return count;
}
// 显示模块列表
void show_module_list(struct module_info *modules, int count,
const struct module_config *config) {
printf("=== 内核模块列表 ===\n");
printf("%-25s %-12s %-8s %-20s %s\n",
"模块名称", "大小", "引用数", "被谁使用", "状态");
printf("%-25s %-12s %-8s %-20s %s\n",
"--------", "----", "----", "------", "----");
int displayed = 0;
for (int i = 0; i < count && displayed < 50; i++) {
struct module_info *mod = &modules[i];
// 如果指定了搜索模式,进行过滤
if (config->search_pattern) {
if (strstr(mod->name, config->search_pattern) == NULL) {
continue;
}
}
// 如果指定了模块名称,精确匹配
if (config->module_name) {
if (strcmp(mod->name, config->module_name) != 0) {
continue;
}
}
printf("%-25s %-12lu %-8d %-20s %s\n",
mod->name, mod->size, mod->ref_count,
mod->used_by, mod->status);
displayed++;
}
if (displayed == 0) {
printf("没有找到匹配的模块\n");
} else if (displayed < count) {
printf("\n显示了 %d 个匹配模块 (总共 %d 个)\n", displayed, count);
}
}
// 显示模块详细信息
void show_module_details(struct module_info *modules, int count,
const struct module_config *config) {
printf("\n=== 模块详细信息 ===\n");
int found = 0;
for (int i = 0; i < count; i++) {
struct module_info *mod = &modules[i];
// 检查是否匹配
if (config->module_name) {
if (strcmp(mod->name, config->module_name) != 0) {
continue;
}
} else if (config->search_pattern) {
if (strstr(mod->name, config->search_pattern) == NULL) {
continue;
}
}
printf("\n模块: %s\n", mod->name);
printf(" 大小: %lu 字节 (%.2f KB)\n", mod->size, (double)mod->size / 1024);
printf(" 引用计数: %d\n", mod->ref_count);
printf(" 被谁使用: %s\n", mod->used_by);
printf(" 状态: %s\n", mod->status);
// 显示 sysfs 信息
char sysfs_path[256];
snprintf(sysfs_path, sizeof(sysfs_path), "/sys/module/%s", mod->name);
if (access(sysfs_path, F_OK) == 0) {
printf(" Sysfs 路径: %s\n", sysfs_path);
// 检查参数
char param_path[256];
snprintf(param_path, sizeof(param_path), "%s/parameters", sysfs_path);
if (access(param_path, F_OK) == 0) {
printf(" ✓ 支持运行时参数配置\n");
}
// 检查引用计数
char refcnt_path[256];
snprintf(refcnt_path, sizeof(refcnt_path), "%s/refcnt", sysfs_path);
FILE *refcnt_fp = fopen(refcnt_path, "r");
if (refcnt_fp) {
char refcnt[32];
if (fgets(refcnt, sizeof(refcnt), refcnt_fp)) {
printf(" 当前引用计数: %s", refcnt);
}
fclose(refcnt_fp);
}
}
found++;
if (config->module_name) {
break; // 只显示指定模块
}
if (found >= 5) {
printf("\n只显示前 5 个匹配模块的详细信息\n");
break;
}
}
if (found == 0) {
if (config->module_name) {
printf("未找到模块: %s\n", config->module_name);
} else if (config->search_pattern) {
printf("未找到匹配模式 '%s' 的模块\n", config->search_pattern);
} else {
printf("没有模块信息\n");
}
}
}
// 显示系统模块统计
void show_module_statistics(struct module_info *modules, int count) {
printf("\n=== 模块统计信息 ===\n");
printf("总模块数: %d\n", count);
if (count > 0) {
// 统计引用计数
int total_refs = 0;
int max_refs = 0;
int zero_refs = 0;
unsigned long total_size = 0;
for (int i = 0; i < count; i++) {
total_refs += modules[i].ref_count;
total_size += modules[i].size;
if (modules[i].ref_count > max_refs) {
max_refs = modules[i].ref_count;
}
if (modules[i].ref_count == 0) {
zero_refs++;
}
}
printf("总大小: %.2f MB\n", (double)total_size / (1024 * 1024));
printf("平均引用计数: %.2f\n", (double)total_refs / count);
printf("最大引用计数: %d\n", max_refs);
printf("零引用模块数: %d\n", zero_refs);
// 显示最常见的模块
printf("\n最常用的模块:\n");
int shown = 0;
for (int i = 0; i < count && shown < 5; i++) {
if (modules[i].ref_count > 0) {
printf(" %s (%d 引用)\n", modules[i].name, modules[i].ref_count);
shown++;
}
}
}
}
// 显示帮助信息
void show_help(const char *program_name) {
printf("用法: %s [选项]\n", program_name);
printf("\n选项:\n");
printf(" -l, --list 列出所有模块\n");
printf(" -d, --details 显示模块详细信息\n");
printf(" -r, --refs 显示引用计数\n");
printf(" -n, --name=MODULE 指定模块名称\n");
printf(" -s, --search=PATTERN 搜索模块名称\n");
printf(" -v, --verbose 详细输出\n");
printf(" -S, --statistics 显示统计信息\n");
printf(" -h, --help 显示此帮助信息\n");
printf("\n示例:\n");
printf(" %s -l # 列出所有模块\n", program_name);
printf(" %s -d -n usbcore # 显示 usbcore 模块详细信息\n", program_name);
printf(" %s -l -s usb # 列出包含 usb 的模块\n", program_name);
printf(" %s -S # 显示模块统计信息\n", program_name);
printf(" %s -d -s network # 显示网络相关模块详情\n", program_name);
}
int main(int argc, char *argv[]) {
struct module_config config = {
.list_modules = 0,
.show_details = 0,
.verbose = 0,
.show_refs = 0,
.show_symbols = 0,
.module_name = NULL,
.search_pattern = NULL
};
printf("=== 内核模块查询工具 ===\n\n");
// 解析命令行参数
static struct option long_options[] = {
{"list", no_argument, 0, 'l'},
{"details", no_argument, 0, 'd'},
{"refs", no_argument, 0, 'r'},
{"symbols", no_argument, 0, 'y'},
{"name", required_argument, 0, 'n'},
{"search", required_argument, 0, 's'},
{"verbose", no_argument, 0, 'v'},
{"statistics", no_argument, 0, 'S'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
int opt;
while ((opt = getopt_long(argc, argv, "ldryn:s:vSh", long_options, NULL)) != -1) {
switch (opt) {
case 'l':
config.list_modules = 1;
break;
case 'd':
config.show_details = 1;
break;
case 'r':
config.show_refs = 1;
break;
case 'y':
config.show_symbols = 1;
break;
case 'n':
config.module_name = optarg;
break;
case 's':
config.search_pattern = optarg;
break;
case 'v':
config.verbose = 1;
break;
case 'S':
config.list_modules = 1;
break;
case 'h':
show_help(argv[0]);
return 0;
default:
fprintf(stderr, "使用 '%s --help' 查看帮助信息\n", argv[0]);
return 1;
}
}
// 如果没有指定操作,默认列出模块
if (!config.list_modules && !config.show_details &&
!config.show_refs && !config.show_symbols) {
config.list_modules = 1;
}
// 显示系统信息
if (config.verbose) {
printf("系统信息:\n");
printf(" 用户 ID: %d\n", getuid());
printf(" 进程 ID: %d\n", getpid());
printf(" 当前目录: %s\n", getcwd(NULL, 0));
printf("\n");
}
// 读取模块信息
struct module_info modules[500];
int module_count = parse_proc_modules(modules, 500);
if (module_count == -1) {
printf("错误: 无法读取模块信息\n");
printf("可能的原因:\n");
printf("1. 系统不支持 /proc/modules\n");
printf("2. 权限不足\n");
printf("3. 内核不支持模块\n");
return 1;
}
if (config.verbose) {
printf("读取到 %d 个模块\n\n", module_count);
}
// 执行相应操作
if (config.list_modules) {
show_module_list(modules, module_count, &config);
}
if (config.show_details) {
show_module_details(modules, module_count, &config);
}
if (optind == 1) { // 如果没有指定参数,显示统计信息
show_module_statistics(modules, module_count);
}
printf("\n=== 模块管理最佳实践 ===\n");
printf("模块安全建议:\n");
printf("1. 只加载可信的模块\n");
printf("2. 定期更新模块\n");
printf("3. 监控模块加载活动\n");
printf("4. 限制模块加载权限\n");
printf("5. 使用签名验证模块\n");
printf("\n");
printf("模块性能优化:\n");
printf("1. 卸载不用的模块\n");
printf("2. 优化模块参数\n");
printf("3. 监控模块内存使用\n");
printf("4. 使用模块黑名单\n");
printf("5. 合理配置模块依赖\n");
printf("\n");
printf("现代替代方案:\n");
printf("1. /proc/modules: 模块列表信息\n");
printf("2. /sys/module/: 模块 sysfs 接口\n");
printf("3. modinfo: 模块详细信息\n");
printf("4. lsmod: 列出模块\n");
printf("5. modprobe: 智能模块加载\n");
printf("6. rmmod: 卸载模块\n");
return 0;
}
编译和运行说明
# 编译示例程序
gcc -o query_module_example1 example1.c
gcc -o query_module_example2 example2.c
gcc -o query_module_example3 example3.c
# 运行示例
./query_module_example1
./query_module_example2
./query_module_example3 --help
./query_module_example3 -l
./query_module_example3 -d -n usbcore
./query_module_example3 -l -s usb
./query_module_example3 -S
系统要求检查
# 检查模块支持
ls /proc/modules
ls /sys/module/
# 检查模块工具
which lsmod
which modinfo
which modprobe
which rmmod
# 查看内核模块目录
ls /lib/modules/$(uname -r)/kernel/
# 检查内核配置
grep -i module /boot/config-$(uname -r)
重要注意事项
- 已废弃:
query_module
在现代 Linux 系统中已废弃 - 权限要求: 某些模块信息需要 root 权限
- 系统依赖: 需要内核支持模块和相应的文件系统接口
- 错误处理: 始终检查返回值和 errno
- 兼容性: 不同内核版本可能有差异
实际应用场景
- 系统监控: 监控内核模块加载和卸载
- 安全审计: 审计系统中加载的模块
- 性能分析: 分析模块对系统性能的影响
- 故障诊断: 诊断模块相关的问题
- 自动化管理: 自动化模块管理脚本
现代替代方案详解
使用 /proc/modules
// 读取模块列表
int read_module_list() {
FILE *fp = fopen("/proc/modules", "r");
if (!fp) return -1;
char line[1024];
while (fgets(line, sizeof(line), fp)) {
char name[256], used_by[256], status[32];
unsigned long size;
int ref_count;
if (sscanf(line, "%255s %lu %d %255s %31s",
name, &size, &ref_count, used_by, status) >= 4) {
printf("%-20s %8lu %3d %s\n", name, size, ref_count, used_by);
}
}
fclose(fp);
return 0;
}
使用 /sys/module/ 接口
// 读取模块详细信息
int read_module_details(const char *module_name) {
char path[256];
// 读取引用计数
snprintf(path, sizeof(path), "/sys/module/%s/refcnt", module_name);
FILE *fp = fopen(path, "r");
if (fp) {
char refcnt[32];
if (fgets(refcnt, sizeof(refcnt), fp)) {
printf("引用计数: %s", refcnt);
}
fclose(fp);
}
return 0;
}
使用命令行工具
# 列出模块
lsmod
# 显示模块信息
modinfo module_name
# 加载模块
sudo modprobe module_name
# 卸载模块
sudo rmmod module_name
# 生成依赖关系
sudo depmod
最佳实践
// 安全的模块信息查询函数
int safe_query_modules(struct module_info *modules, int max_count) {
// 验证参数
if (!modules || max_count <= 0) {
errno = EINVAL;
return -1;
}
// 检查文件存在性
if (access("/proc/modules", F_OK) != 0) {
errno = ENOENT;
return -1;
}
// 读取模块信息
return parse_proc_modules(modules, max_count);
}
// 模块安全检查
int validate_module_security(const char *module_name) {
// 检查模块名称合法性
if (!module_name || strlen(module_name) == 0) {
return -1;
}
// 检查是否在黑名单中
// 这里简化处理,实际应用中可以检查 /etc/modprobe.d/blacklist.conf
const char *blacklisted[] = {"firewire-sbp2", "usbmouse", NULL};
for (int i = 0; blacklisted[i]; i++) {
if (strcmp(module_name, blacklisted[i]) == 0) {
printf("警告: 模块 '%s' 在黑名单中\n", module_name);
return -1;
}
}
return 0;
}
这些示例展示了 query_module
函数的概念以及现代 Linux 系统中查询内核模块信息的各种替代方案,帮助你全面了解 Linux 系统中的模块管理机制。