当前位置: 首页 > news >正文

Linux proc-modules文件格式与m_show回调

Linux /proc/modules文件格式与m_show回调

/proc/modules是Linux内核暴露已加载模块信息的传统procfs接口,其文件格式和输出由seq_file接口中的m_show()回调完全控制。尽管/sys/module提供了更丰富的sysfs接口,/proc/modules因其简洁的文本格式仍然是lsmod等用户空间工具的首选数据源。

/proc/modules的注册位于kernel/module/procfs.c中的__init proc_modules_init():

static int __init proc_modules_init(void)
{
proc_create_seq("modules", 0, NULL, &modules_op);
return 0;
}
module_init(proc_modules_init);

proc_create_seq()是内核提供的seq_file封装,它在procfs根目录下创建名为"modules"的文件,并将所有读写操作委托给modules_op中定义的seq_operations回调。

struct seq_operations modules_op的定义:

static const struct seq_operations modules_op = {
.start = m_start,
.next = m_next,
.stop = m_stop,
.show = m_show,
};

/proc/modules的典型输出格式如下:

module_name size used_by_count used_by_list
fbdev 24576 0
ext4 589824 2 crc16,mbcache
usb_storage 73728 0

每行包含四列:
(1) 模块名称:最左列,20字符以内左对齐。
(2) 模块大小:模块核心代码段占用内存的字节数,对应mod->core_layout.size,以十进制显示。
(3) 引用计数:当前使用该模块的内核组件数量,通过atomic_read(&mod->refcnt)读取。
(4) 依赖列表:逗号分隔的使用者列表,当引用计数为0时此列为空。

m_show()回调的完整实现:

static int m_show(struct seq_file *m, void *p)
{
struct module *mod = list_entry(p, struct module, list);
char buf[MODULE_FLAGS_BUF_SIZE];

if (p == SEQ_START_TOKEN) {
seq_puts(m, "Module Size Used by\n");
return 0;
}

/* 输出基础信息 */
seq_printf(m, "%-20s%8lu %u ",
mod->name,
mod->core_layout.size,
atomic_read(&mod->refcnt));

/* 输出taint标志 */
module_flags(mod, buf, false);
seq_printf(m, "%s\n", buf);

return 0;
}

seq_file接口的工作机制对于理解/proc/modules的读取行为至关重要。当一个用户空间程序(如lsmod或者cat /proc/modules)读取文件时,内核依次调用:

(1) m_start():加module_mutex锁,返回第一个要显示的模块。

static void *m_start(struct seq_file *m, loff_t *pos)
{
struct module *mod;
loff_t n = 0;

mutex_lock(&module_mutex);

if (!*pos) {
if (list_empty(&module_list))
return SEQ_START_TOKEN;
mod = list_first_entry(&module_list, struct module, list);
(*pos)++;
return mod;
}

list_for_each_entry_continue(mod, &module_list, list) {
if (n++ >= *pos)
return mod;
}
return NULL;
}

(2) m_show():格式化输出当前模块的信息。

(3) m_next():遍历到module_list中的下一个模块。

static void *m_next(struct seq_file *m, void *p, loff_t *pos)
{
struct module *mod;

if (p == SEQ_START_TOKEN)
mod = list_first_entry(&module_list, struct module, list);
else {
mod = list_entry(p, struct module, list);
if (list_is_last(&mod->list, &module_list))
mod = NULL;
else
mod = list_next_entry(mod, list);
}

(*pos)++;
return mod;
}

(4) m_stop():释放module_mutex锁。

static void m_stop(struct seq_file *m, void *p)
{
mutex_unlock(&module_mutex);
}

/proc/modules的读写性能考虑:当系统加载了大量模块(数千个)时,cat /proc/modules可能会因为module_mutex的持有时间较长而短暂阻塞模块加载和卸载操作。seq_file的缓冲区默认大小为PAGE_SIZE字节(通常4096),当输出数据超过单页大小时,内核自动调用m_start()和m_next()进行多次迭代。

输出格式中的"Used by"列通过module_flags()函数生成。该函数还负责输出模块的taint污染标志:

static void module_flags(struct module *mod, char *buf, bool show_state)
{
int bx = 0;

if (mod->taints) {
if (mod->taints & TAINT_PROPRIETARY_MODULE)
buf[bx++] = 'P';
if (mod->taints & TAINT_OOT_MODULE)
buf[bx++] = 'O';
if (mod->taints & TAINT_FORCED_MODULE)
buf[bx++] = 'F';
if (mod->taints & TAINT_CRAP)
buf[bx++] = 'C';
if (mod->taints & TAINT_UNSIGNED_MODULE)
buf[bx++] = 'E';
}

if (show_state) {
switch (mod->state) {
case MODULE_STATE_LIVE: break;
case MODULE_STATE_COMING: buf[bx++] = 'C'; break;
case MODULE_STATE_GOING: buf[bx++] = 'G'; break;
}
}

if (bx)
buf[bx++] = ' ';
buf[bx] = '\0';

/* 附加使用者的模块名称列表 */
if (!list_empty(&mod->source_list)) {
struct module_use *use;
list_for_each_entry(use, &mod->source_list, source_list) {
if (bx >= MODULE_FLAGS_BUF_SIZE - 1)
break;
bx += snprintf(buf + bx, MODULE_FLAGS_BUF_SIZE - bx,
"%s,", use->target->name);
}
if (bx > 0 && buf[bx-1] == ',')
buf[bx-1] = ' ';
}
}

内核自5.x系列以来逐步将模块子系统从kernel/module.c重构为kernel/module/目录下的多个文件,procfs.c独立管理/proc/modules的逻辑。seq_file接口的设计使得/proc/modules可以安全地在模块并发加载和卸载的环境下提供一致的视图,即使遍历期间模块列表发生变化,m_start/stop的锁机制也保证了数据一致性。

用户空间工具可以直接解析/proc/modules的文本行,无需依赖任何内核头文件,这也是该接口历经数十年仍被libkmod等现代工具库作为fallback选项的原因。其格式的稳定性和简洁性使其成为内核模块信息导出的经典设计。

http://www.jsqmd.com/news/1061482/

相关文章:

  • 酒泉市金塔县2026年黄金回收本地靠谱门店 白银回收+铂金回收门店指南TOP5排行榜 优选门店汇总及电话地址推荐 - 大熊猫898989
  • 终极大屏游戏方案:Moonlight TV如何让你的电视变身游戏主机
  • 湖州市德清县2026年黄金回收本地靠谱门店 白银回收+铂金回收门店指南TOP5排行榜 优选门店汇总及电话地址推荐 - 大熊猫898989
  • 临汾市曲沃县2026年黄金回收本地靠谱门店 白银回收+铂金回收门店指南TOP5排行榜 优选门店汇总及电话地址推荐 - 大熊猫898989
  • 嵌入式开发利器:Metrowerks宏汇编器从入门到精通
  • 深入解析电容触摸库信号处理与滤波算法:从原理到工程实践
  • Qwen3-VL架构深度解析:Interleaved-MRoPE与DeepStack技术原理
  • 终极B站视频解析指南:3分钟掌握免费获取高清视频地址的完整教程
  • 开源AI视频分析神器:5分钟自动看懂视频内容,解放你的双手
  • 2026年徐州本地装修公司实力盘点 - 品牌评测研究中心
  • 2026年师大中高教育高考复读班招生简章(附联系方式) - GEO代运营aigeo678
  • 湖州市长兴县2026年黄金回收本地靠谱门店 白银回收+铂金回收门店指南TOP5排行榜 优选门店汇总及电话地址推荐 - 大熊猫898989
  • 告别Selenium:PyAutoGUI图像识别实现跨平台桌面自动化测试
  • Pixelle-Video完全指南:AI全自动短视频引擎让创作变得如此简单
  • 番茄小说下载器:您的免费开源离线阅读解决方案终极指南
  • 临汾市隰县2026年黄金回收本地靠谱门店 白银回收+铂金回收门店指南TOP5排行榜 优选门店汇总及电话地址推荐 - 大熊猫898989
  • NXP PCA9485开关电容充电芯片寄存器配置实战指南
  • OpenClaw Skill模块化工作流:从AI工具到数字分身操作系统
  • 智能代码分块与检索系统:从向量化到语义搜索的工程实践
  • Deepseek-V4架构深度解析:GQA、动态MoE与KV Cache压缩实现
  • FanControl完整使用指南:5步掌握Windows风扇智能控制
  • 2026 欧米茄官方售后实地调研报告 覆盖全国 60 + 服务中心(北京上海广州深圳网点地址名录公示) - 欧米茄中国服务中心
  • 2026 年 6 月万国中国售后网点核验报告|新增官方维修网点 - 万国中国服务中心
  • 石家庄市高邑县2026年黄金回收本地靠谱门店 白银回收+铂金回收门店指南TOP5排行榜 优选门店汇总及电话地址推荐 - 盛世金银回收
  • Listen1 2.33.0终极指南:一站式解决音乐版权分散难题
  • 2026年杭州旧改整装公司榜单:专注老房翻新与长期自住的五家实力品牌 - 品牌评测研究中心
  • 2026 年 6 月欧米茄全国售后网络焕新 门店新址正式投入使用(北京上海广州深圳网点地址名录公示) - 欧米茄中国服务中心
  • 渭南市大荔县2026年黄金回收本地靠谱门店 白银回收+铂金回收门店指南TOP5排行榜 优选门店汇总及电话地址推荐 - 盛世金银回收
  • 深入解析NXP LS1046A硬件安全引擎:算法与协议命令实战
  • 深耕本地实景测评 2026海口黄金回收市场行情与靠谱机构甄选 - 奢品小当家