news 2026/10/10 6:44:07

Linux depmod命令详解:内核模块依赖关系与加载实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux depmod命令详解:内核模块依赖关系与加载实战指南

1. 认识depmod:内核模块系统里那个“劳碌命”的幕后管家

1.1 先搞明白它在Linux系统里到底做什么

经常折腾内核模块、驱动编译或者嵌入式Linux的朋友,肯定绕不开这么一条命令:

depmod -a

看名字就知道,depmod = dependency + module,核心工作就是帮内核模块(.ko文件)生成依赖关系。Linux的模块系统并不是简单地把所有.ko文件丢进/lib/modules/$(uname -r)/就完事了,模块之间经常互相引用符号,加载时存在先后顺序,内核需要知道“谁先加载、谁后加载、谁依赖谁”。

depmod干的事情,就是扫描指定内核版本目录下的所有模块,分析它们导出的符号和使用到的外部符号,然后生成一份关系清单,存成这些文件:

  • modules.dep—— 纯文本依赖关系表,一行一个模块,冒号后面跟着它依赖的其他模块;
  • modules.dep.bin—— 上面这份文件的二进制压缩版,快速读取用;
  • modules.symbols—— 模块导出的符号表,modprobe通过它才能按符号名找到对应模块;
  • modules.alias—— 设备别名表,硬件设备识别和自动加载时靠它;
  • modules.builtin/modules.builtin.modinfo—— 编译进内核的模块记录,用于避免重复加载。

这套文件其实就是模块加载器的“索引”和“导航图”。没有它们,modprobe基本就是瞎的。你手动insmod一个模块或许还能勉强跑起来,但一旦遇到依赖链,立刻原形毕露。

1.2 为什么内核模块机制不能没有依赖分析

内核模块之间是“符号引用”的关系。举个很直白的例子:你写了一个字符设备驱动mydev.ko,它调用了kernel里某个EXPORT_SYMBOL导出的函数;而另一个驱动other.ko又依赖mydev.ko里的一个函数。这种情况下,加载顺序必须是:

内核基础符号 → mydev.ko → other.ko

depmod把这种链条扫出来,记录进modules.dep。之后你只要执行modprobe other,它就会自动把mydev.ko先拉起来,根本不需要你操心先后顺序。

常见的热搜词里能看到大量linux驱动、嵌入式linux、linux系统管理、linux运维故障案例相关的搜索需求,其实都和模块加载脱不了干系。很多运维同学在服务器上编译安装驱动,或者嵌入式工程师交叉编译完驱动模块拷到板子上,第一步就是insmod然后报错:Unknown symbol。那个报错的本质,就是内核符号表里找不到模块依赖的符号——要么模块没加载,要么依赖关系没建立。

2. 命令语法和常用参数拆解

2.1 先看完整语法格式

depmod的语法并不复杂,通用格式如下:

depmod [选项] [内核版本]

如果不指定内核版本,它默认操作当前正在运行的内核对应的模块目录/lib/modules/$(uname -r)。如果在嵌入式开发环境里交叉分析,你可以用-b指定根目录。

执行depmod -a时会经历这几个过程:

  1. 确定目标内核目录;
  2. 递归遍历目录下全部.ko文件;
  3. 打开每个模块文件,读取ELF结构里的.modinfo段和符号表;
  4. 构建符号依赖图,生成依赖文件;
  5. 将结果写入目录顶部(或指定目录)。

2.2 必知必会的常用参数

表格里整理了我实际使用中频率最高的几个参数:

参数作用典型使用场景
-a/--all扫描目标目录下所有模块新装驱动模块后刷新依赖
-A/--quick只对比文件修改时间,有变化才重新生成脚本里快速检查更新
-b <目录>/--basedir指定根目录(常用于交叉编译环境)嵌入式开发,分析目标板rootfs
-F <System.map>指定内核符号表文件分析外部内核符号依赖时必须用
-n/--dry-run只打印结果不写文件检查依赖关系是否正确
-w扫描时同时输出警告信息排查未知符号告警

这里特别强调-F System.map。很多人交叉编译内核模块到ARM板子时,直接执行depmod -b /path/to/rootfs,结果模块依赖信息不正确,或者出现大量Unknown symbol警告。原因就是depmod没有拿到目标内核的System.map符号表,无法判断哪些符号是内核本身提供的。正确姿势是加-F:

depmod -b /path/to/rootfs -F /path/to/kernel/System.map 4.19.xx

3. 实操上手:从编译模块到正确刷新依赖

3.1 完整链路:编译 → 安装 → depmod → modprobe

我见过太多新手在编译模块后直接手动把.ko拷进/lib/modules/$(uname -r)/,然后运行insmod各种报错,或者modprobe直接提示找不到模块。问题就出在漏了depmod这一步。

正确流程应该是下面这样:

  1. 编译模块:
make -j$(nproc)
  1. 安装模块到系统目录:
sudo make modules_install

这个操作会把模块复制到/lib/modules/$(uname -r)/extra/或对应子目录,同时生成modules.order和modules.builtin等文件。

  1. 刷新模块依赖关系:
sudo depmod -a
  1. 加载模块:
sudo modprobe mymodule

make modules_install这一步很多发行版并不会自动执行depmod,所以第3步必不可少。如果不刷,modprobe mymodule大概率提示:

modprobe: FATAL: Module mymodule not found in directory /lib/modules/5.15.0-xxx

模块明明就在目录里,却说找不到,原因就是modules.dep里没有索引记录。

3.2 手动拷贝模块时的处理方式

如果不想执行完整的make modules_install,而是自己把编译好的mymodule.ko放到某个目录下,比如/lib/modules/$(uname -r)/extra/,那也要重新刷新:

sudo mkdir -p /lib/modules/$(uname -r)/extra sudo cp mymodule.ko /lib/modules/$(uname -r)/extra/ sudo depmod -a sudo modprobe mymodule

注意modprobe默认搜索路径是根据模块目录递归扫描的,不局限于某个子目录。所以只要你放对位置并刷新,指定模块名(不带.ko后缀)就能加载。

再补充一句:模块名的连字符和下划线问题也常让人踩坑。内核模块文件叫my-module.ko,但modprobe查询时用的是my_module,因为内核模块名规范是统一把-转换成_。比如modprobe my-module和modprobe my_module效果一样。

3.3 使用-A快速刷新和-n预检查

有些场景下全量扫描所有模块很浪费时间,尤其服务器上模块数量上千。如果只是刚刚放入一个新模块,可以用-A只扫描有变动的模块:

sudo depmod -A

它的机制是比对.modinfo的mtime,只处理新文件或修改过的文件,速度快很多。

在改动系统前如果想先看看结果对不对,用-n空转一把:

depmod -n | head -50

这条命令会读取模块并打印出即将生成的modules.dep内容,但不会实际改动任何文件。对于谨慎型选手和运维变更前检查,非常好用。

4. 场景实战:嵌入式开发、内核升级与自动加载

4.1 交叉编译环境里依赖关系分析的完整操作

嵌入式Linux开发中,我们经常在x86主机上交叉编译出arm架构的.ko,再拷贝到开发板的rootfs里用。这时候不能直接跑宿主机上的depmod -a,而是要用-b指定目标板的根目录。

假设目标系统rootfs放在/home/user/rootfs,内核版本是4.19.100,交叉编译出hello_drv.ko并拷贝到/home/user/rootfs/lib/modules/4.19.100/,那么正确刷新依赖执行的命令是:

depmod -b /home/user/rootfs -F /home/user/kernel/System.map 4.19.100

-b告诉depmod把/home/user/rootfs当作/来看待,所以内部路径会映射到/home/user/rootfs/lib/modules/4.19.100/。-F指定System.map,让depmod知道内核自身提供的符号集合,避免把内核符号误判为模块依赖。

这一步漏了-F,最常见的现象就是生成的modules.dep里,明明只是依赖内核导出的函数,却被写成依赖某个根本不存在的模块,甚至直接报警告:

WARNING: Symbol 'some_func' not found in kernel module directory

到板子上modprobe自然失败。嵌入式平台踩这个坑的人特别多。

4.2 内核升级后没有刷新模块依赖的情况

升级内核也是depmod的高频触发场景。安装新内核后,初始ramdisk和模块目录针对不同内核版本,操作指令稍有区别。

假设新内核版本是6.6.1,安装完成后你检查/lib/modules/6.6.1/,发现模块文件都在,但目录下没有modules.dep。此时:

sudo depmod -a 6.6.1

指定内核版本号,就会只对6.6.1目录生成依赖文件。升级后用uname -r确认当前内核版本,再加载第三方驱动模块之前,一定要确保当前版本的目录依赖是完整的。

另外注意,depmod写依赖文件时,会把模块路径写成绝对路径。如果你在chroot环境或容器里操作,输出路径可能和你预期的不一样,这时-b同样能兜底。

4.3 结合modprobe理解完整加载链

为了彻底理解depmod的用处,我建议你手动走一遍加载链,观察各环节的文件变化。

执行strace modprobe 8139cp 2>&1 | grep modules就能看到modprobe读取的关键文件。正常流程大概是:

  1. 查找modules.dep,看目标模块是否存在及依赖;
  2. 读取modules.symbols,解析符号与模块映射;
  3. 通过modules.alias做设备别名匹配;
  4. 按顺序加载依赖模块;
  5. 最后加载目标模块本体。

一旦modules.dep缺失或内容不正确,modprobe会立刻报模块找不到。所以你有没有发现,modprobe本质上只做“查表+依赖顺序加载”,真正的“侦察兵”工作是depmod干的。

5. 常见问题与排查技巧实录

5.1 模块存在但modprobe提示找不到

这类报错在运维和嵌入式群里几乎每周都有人问:

modprobe: FATAL: Module xxx not found in directory /lib/modules/5.15.0-91-generic

排查思路按顺序来:

  1. 确认模块文件确实存在于模块目录里:find /lib/modules/$(uname -r) -name "*.ko" | grep xxx;
  2. 确认modules.dep是否包含该模块:grep xxx /lib/modules/$(uname -r)/modules.dep;
  3. 如果grep结果为空,执行sudo depmod -a后再次验证;
  4. 手动尝试sudo insmod /绝对路径/xxx.ko看是否还有别的报错。

看到这里你应该明白了:第2步和第3步就是最核心的排查动作。我建议在每个模块目录变更后,都用depmod -a刷新一次,宁可多跑一次也不留隐患。

5.2 Unknown symbol错误分析

insmod一个模块时报:Unknown symbol。这是模块使用了内核或其他模块提供的符号,但当前环境中没有对应提供者。

排查步骤:

# 查看模块依赖了哪些符号、由谁提供 modinfo xxx.ko nm xxx.ko | grep ' U '

nm输出中带U标记的就是未定义符号。然后用:

grep 符号名 /lib/modules/$(uname -r)/modules.symbols

看这个符号是否被某个模块导出。如果grep为空,说明符号不存于任何模块,那是内核符号缺失;如果grep到了,再确认对应模块有没有在modules.dep中挂好依赖,或者是否已被成功加载。

在实际设备上,还有一种常见情况是编译模块的主机内核版本和运行环境不一致,导致ABI不匹配、符号地址错位。这种问题depmod没法救,只能重新用目标环境的内核源码或headers编译。

5.3 嵌入式设备上depmod不存在怎么办

很多精简的嵌入式rootfs里根本没有depmod工具,特别是用busybox裁剪出来的系统。替代思路有几种:

  1. 在宿主机上对rootfs执行带-b的depmod,生成好依赖文件再打包进rootfs;
  2. 用busybox depmod,支持基础功能;
  3. 交叉编译depmod工具放进板子;
  4. 模块数量很少时,手工编辑modules.dep内容。

我实际做量产系统时,都是在PC端交叉分析完成后,把整个/lib/modules/$(uname -r)目录打包进rootfs,不在板子上跑depmod,效率和可靠性都更好。

5.4 权限和文件属性导致的隐藏异常

还有个别案例比较坑:模块文件权限不对。.ko文件如果所有者和权限异常,depmod虽然能扫描,但modprobe加载时可能由于模块自身vermagic或文件读取问题而失败。

检查方法:

ls -l /lib/modules/$(uname -r)/extra/xxx.ko file /lib/modules/$(uname -r)/extra/xxx.ko

file命令输出里能看到模块是ELF 64-bit还是32-bit,以及架构信息。32位模块放到64位内核里,depmod都会直接跳过或者报警告,后面加载更是必然会失败。

6. 经验心得与进阶操作

6.1 我用depmod踩过最深的坑

聊个我自己的案例。当时在给一块ARM开发板编写SPI驱动,交叉编译完成后把.ko丢进板子/lib/modules/4.19.100/,执行modprobe spi_test报Module not found,但ls明明能看到文件。

排查了一圈发现,问题出在我交叉编译时用的是宿主机的/lib/modules下的depmod,没有用-b指定rootfs,导致依赖文件里记录的都是宿主机路径。比如kernel/drivers/spi/spidev.ko这种相对路径少写了一大截,板子上自然找不到。

后来养成的习惯是:任何嵌入式rootfs的模块操作,都写成一条固定命令:

depmod -b <rootfs路径> -F <内核System.map> <内核版本>

再也没在这上面翻过车。

6.2 把这套逻辑用到内核开发调试中

如果你在开发自己的内核模块,depmod -n是非常好的调试伙伴。每次改完模块代码重编译后,先跑一次:

depmod -n | grep 模块名

看看新模块是否被正确索引、依赖了哪些模块。它能帮你在insmod之前就发现依赖关系的异常。

如果模块开发中用到EXPORT_SYMBOL导出给别的模块使用,别忘了刷新后检查modules.symbols内容:

grep 导出的符号名 /lib/modules/$(uname -r)/modules.symbols

查到记录,说明外部模块才能正常引用它。查不到,说明导出有问题,后面写测试模块怎么也调不通。

6.3 简化运维:把depmod固化到自动化和脚本里

对运维同学来说,与其每次都手动执行,不如放进初始化脚本或Ansible这类自动化工具里。我自己常用的一个习惯是在安装任何第三方驱动后,统一执行:

sudo depmod -a sudo modprobe -r 旧模块 2>/dev/null; sudo modprobe 新模块

脚本里注意一个细节:depmod -a执行时如果恰好有模块正在被使用,并不会造成破坏,但如果你紧接着modprobe -r卸载正在运行的模块,业务就中断了。变更窗口内操作,并且先确认没有进程占用模块:

lsmod | grep 模块名

6.4 不引入新文件直接检查系统的方法

最后一个实用技巧:在不做任何修改的前提下快速判断系统模块依赖是否完整,可以用:

test -f /lib/modules/$(uname -r)/modules.dep && echo "modules.dep exists"

如果要对比两个内核版本的依赖文件是否一致:

diff /lib/modules/5.15.0/modules.dep /lib/modules/6.6.1/modules.dep | head

这在升级回滚场景里很有用。回滚内核时如果旧版本依赖文件被误删或覆盖,提前发现能避免重启后网络模块加载失败、网卡起不来的尴尬。

最后再说一句,我个人的经验是:任何涉及模块增删、内核版本变化、rootfs构建的场景,都默认把depmod -a当成和cp、make一样不可或缺的一步。这条命令平时毫无存在感,可一旦你跳过它,各种诡异报错会在深夜加班时挨个找上门来。把这篇文章收藏起来,多实操几遍,把这些“坑”提前踩平,后面你会感谢现在的自己。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/10 6:43:32

CentOS 7虚拟机网络配置全攻略:NAT模式与固定IP实战

CentOS 7虚拟机装好了&#xff0c;进入系统第一件事就是配置网络。很多人卡在第一步&#xff1a;ping www.baidu.com死活不通&#xff0c;报错信息换来换去&#xff0c;就是找不着原因。这种场景我见了太多次&#xff0c;几乎每个刚接触虚拟机装Linux的人都会在"配置网络&…

作者头像 李华
网站建设 2026/10/10 6:42:44

LeetCode 27 移除元素:从暴力到双指针的原地操作详解

Day 24了&#xff0c;今天刷到LeetCode第27题“移除元素”。这道题很多初学者一看就觉得简单&#xff1a;不就是把数组里等于某个值的元素删掉吗&#xff1f;但实际动手写的时候&#xff0c;问题就来了——原地操作不允许开新数组、返回的是新长度而不是新数组、删除元素后下标…

作者头像 李华
网站建设 2026/10/10 6:42:28

AnyPS5:PS5存档管理工具设计与备份校验实践

1. “AnyPS5”这个名字背后&#xff1a;一次存档管理的重构实践大概半年前&#xff0c;我在整理手头几台PS5主机时&#xff0c;遇到了一个几乎所有多机党、多账号用户都会撞上的麻烦&#xff1a;存档东一个西一个&#xff0c;备份文件散落在不同硬盘里&#xff0c;命名全靠“日…

作者头像 李华
网站建设 2026/10/10 6:42:25

机房UPS选型全攻略:从负载计算到冗余架构

1. 不同规模机房的选型思路拆解1.1 先认清机房的“规模”本质做UPS选型这么多年&#xff0c;我最大的感受是&#xff1a;很多人都把注意力放在“买多大功率”上&#xff0c;却忽略了机房规模背后的本质差异。机房的规模不应该只看物理面积&#xff0c;更应该看IT负载总量和可用…

作者头像 李华
网站建设 2026/10/10 6:42:11

2024年VScode配置Python开发环境完整指南:从解释器到依赖锁定

简介&#xff1a;面向希望在VSCode中高效搭建Python开发环境的初学者与进阶用户&#xff0c;这份资源以2024年最新实践为基础&#xff0c;系统整理了从安装解释器、管理虚拟环境到配置调试器、集成Git与常用插件的完整流程。压缩包共152个文件&#xff0c;约3.54MB&#xff0c;…

作者头像 李华
网站建设 2026/10/10 6:42:06

Spring Boot + Vue 高校医务室预约系统全栈开发实践

做高校医务室预约系统这类项目的同学和同行&#xff0c;这几年是越来越多了。原因其实很现实&#xff1a;Spring Boot Vue 这套前后端分离组合&#xff0c;既能支撑起一个完整可运行的业务项目&#xff0c;也能在简历里讲成有真实使用场景的作品&#xff0c;而高校医务室的业务…

作者头像 李华