最近又踩了一次buildroot下modprobe: cant open modules.dep: No such file or directory的坑正好趁这个机会把这个问题彻底掰开揉碎讲一遍。场景很典型我用buildroot给一块ARM开发板定制系统内核和rootfs都是同一套构建流程产出的板子起来之后无线网卡驱动没自动加载。我手动执行modprobe想把驱动拉起来结果shell直接甩了这句话。第一反应是驱动模块没编出来但去/lib/modules/目录一看.ko文件明明躺在那里模块本身没有任何问题问题就出在modules.dep这一层。如果你也遇到同样的报错先别急着怀疑模块代码、工具链或者内核配置绝大多数情况下是构建流程、rootfs打包和内核版本匹配这三件事里有一环没对上。下面我从报错本身的含义讲起把完整的排查思路和修复手段都过一遍。1. 先读懂这个报错modprobe在找什么为什么找不到1.1 modules.dep是什么modules.dep是内核模块的依赖关系描述文件由depmod工具扫描指定目录下所有.ko文件后生成位置固定在内核模块目录下/lib/modules/$(uname -r)/modules.dep文件内容长这样/kernel/drivers/net/wireless/xxx/xxx.ko: /kernel/drivers/net/wireless/yyy/yyy.ko: /kernel/drivers/net/wireless/xxx/xxx.ko每一行是一个模块路径加冒号冒号后面列出它依赖的其他模块。第二行的意思是加载yyy.ko之前必须先加载xxx.ko。这个文件描述的是/lib/modules/$(uname -r)/目录下的相对关系路径不写绝对路径前缀因为modprobe会自动以模块目录作为根目录去解析。除了modules.depdepmod还会生成modules.alias、modules.symbols、modules.builtin等文件分别服务于模块别名匹配、符号查找和编入内核的模块记录。modprobe在解析你要加载什么模块这个问题时依赖的就是这一整套元数据其中modules.dep是核心。1.2 modprobe和insmod的区别很多人只知道modprobe是加载模块的命令但不知道它和insmod的本质差异。insmod的行为非常直白给你指定路径的.ko文件加载它。它是sys_init_module系统调用的一个薄封装不解析依赖不查别名不做任何智能处理。如果模块A依赖模块B而你只insmod A.ko内核会直接报Unknown symbol因为符号解析不到。modprobe则完全不同。它做的事情可以拆成三步第一根据模块名在modules.alias和modules.dep里定位真正的.ko文件路径第二解析这个模块依赖了哪些其他模块递归地把依赖先加载好第三再加载目标模块。整个过程都依赖modules.dep提供依赖关系信息。所以一旦modules.dep缺失modprobe第一步就卡死了直接报出标题里那个错。打个比方insmod是你手动把每个零件按顺序装到机器上modprobe是拿着装配图自动装。modules.dep就是那张装配图。1.3 命令本身缺没缺这是两码事这里要厘清一个容易混淆的点。报错信息modprobe: cant open modules.dep: No such file or directory意味着modprobe命令本身是存在的、可执行的它在运行过程中找不到目标文件。如果系统里压根没有modprobe命令shell会提示的是modprobe: not found或者command not found那才是缺少命令。buildroot里modprobe命令通常来自两个地方busyboxbuildroot默认集成busyboxbusybox的modprobeapplet默认是开启的提供基本的模块加载能力kmod如果启用了BR2_PACKAGE_KMOD_TOOLS会安装完整的modprobe和depmod功能比busybox版本更全对模块依赖的处理也更完善。如果你用到的是精简过的busybox配置恰好把CONFIG_MODPROBE关掉了那就会出现命令缺失。这个可以通过make busybox-menuconfig勾选回modprobe解决或者干脆启用kmod工具两条路都能拿到modprobe命令。2. buildroot里谁负责生成modules.dep一条容易被忽视的流水线2.1 从内核配置到模块落地的完整步骤在buildroot中构建内核和生成rootfs镜像是一个串联流程。Linux内核包的安装步骤会做几件事编译内核镜像zImage/Image等和设备树如果内核配置了CONFIG_MODULESy执行make modules_install把所有以M状态编译的模块安装到output/target/lib/modules/release/目录下调用主机端的depmod工具以output/target为根目录为release这个版本生成modules.dep和配套元数据文件之后buildroot再根据你选择的rootfs格式ext4、cpio、tar等把整个output/target目录打包成最终镜像。步骤2和步骤3不是恒定执行的它们有一个前提条件内核配置里打开了CONFIG_MODULESy。如果你用make linux-menuconfig进入内核配置后在General setup里没有勾选Enable loadable module support那么无论你在驱动目录里选了多少M这些模块都不会被编译更不会进入rootfs。buildroot在构建linux包时会去读取内核的.config文件检测到CONFIG_MODULESy才会执行模块安装和depmod这两步是绑定在一起的。2.2 改过内核配置为什么modules.dep还是没生成这个坑我踩过不止一次属于buildroot使用者的高频翻车点。你执行make linux-menuconfig把某个驱动从*改成了M保存退出然后直接执行make想重新打包rootfs。结果发现要么模块没进rootfs要么进了但没有modules.depmodprobe照样报错。原因在于buildroot对linux包有自己的一套依赖判断逻辑它会比对源码状态、配置文件、构建产物的时间戳。在有些情况下单纯跑顶层make并不会重新触发linux包的安装步骤特别是当你只是改了内核配置而没有触发源码目录变化时。buildroot可能认为linux包没有需要重做的部分直接跳过了安装环节自然也不会执行modules_install和depmod。解决方法很简单用make linux-rebuild强制重建linux包make linux-rebuild这个命令会强制重新执行linux包的编译和安装流程包括重新安装内核模块、重新生成modules.dep。执行完之后再跑一次完整的make让buildroot把最新的output/target打包成rootfs镜像。这一套组合基本能解决改了配置但模块依赖文件不刷新的问题。2.3 外部内核模块包也是重灾区如果你的驱动不是内核自带的而是以buildroot外部包的形式加入常见的做法是写一个xxx.mk在编译后调用内核的模块构建机制那modules.dep是否生成就取决于你在这个包里的安装脚本怎么写。很多从旧工程或者网上抄来的.mk文件只做了模块安装没有顺手调用depmod。典型的写法是define MYDRV_INSTALL_TARGET_CMDS $(MAKE) -C $(D) modules_install \ INSTALL_MOD_PATH$(TARGET_DIR) endef这样模块文件确实被放进了/lib/modules/release/但modules.dep可能没有被更新甚至完全缺失。正确做法是在安装步骤里追加一次depmod调用或者在.mk里使用buildroot提供的kernel-module基础设施让框架自动处理依赖文件。业内很多驱动仓库的buildroot集成包就是这么处理的但不同buildroot版本的变量名和工具路径有差异直接照抄网上代码时要格外注意版本适配。3. 现场排查从板上现象反推问题出在哪一环遇到这类报错不要急着重建系统也别一头扎进驱动源码里。按顺序做三个检查几分钟就能把问题范围缩小到具体环节。3.1 第一步确认modprobe是谁提供的在开发板的终端上执行which modprobe ls -l /sbin/modprobe /bin/modprobe 2/dev/null正常情况下你应该能看到busybox或者kmod提供的符号链接。如果shell提示command not found说明rootfs里压根没带modprobe命令那就需要回buildroot里把busybox的modprobeapplet打开或者启用kmod工具。如果命令存在继续下一步。3.2 第二步对比uname -r和/lib/modules这一步是整个排查链路里最容易被忽略的但也是最高频的根因。在开发板上执行uname -r ls /lib/modules/对比两个命令的输出。如果uname -r显示的内核版本和/lib/modules/下的目录名不一致那modules.dep缺失只是表象真正的问题是rootfs里的模块版本和实际启动的内核版本对不上。举个例子buildroot构建内核时带了CONFIG_LOCALVERSION-custom那么模块会安装到/lib/modules/5.10.0-custom/depmod生成的也是这个目录下的modules.dep。但如果bootloader加载的Image是另一个版本比如5.10.0那么内核运行时的uname -r是5.10.0modprobe会去/lib/modules/5.10.0/找modules.dep。目录都不存在自然报cant open modules.dep。3.3 第三步回到主机看output/target如果板子和buildroot构建环境是同一套直接看构建机上的output/target目录ls -l output/target/lib/modules/ find output/target/lib/modules -name modules.dep这里会出现三种情况output/target下没有modules.dep说明问题出在buildroot构建环节需要检查内核配置和构建流程output/target下有modules.dep但最终烧录到板子上的rootfs镜像里没有说明问题出在打包或者烧写环节比如rootfs镜像大小限制导致lib/modules被裁剪或者烧写时漏掉了这个目录output/target和板上都有modules.dep但modprobe依然报错那就要检查uname -r版本是否匹配以及rootfs是否以只读方式挂载导致模块目录不可读。3.4 用一张对照表快速判断根因现场现象大概率根因解决方向/sbin/modprobe不存在busybox/kmod未启用modprobemake busybox-menuconfig勾选modprobe或启用BR2_PACKAGE_KMOD_TOOLSuname -r与/lib/modules不一致内核镜像与rootfs版本不配套统一内核版本或调整CONFIG_LOCALVERSION后重新构建output/target下没有modules.dep构建流程没执行depmodmake linux-rebuild后再执行完整makeoutput/target有但镜像里没有打包/烧写环节丢了lib/modules检查rootfs大小限制、分区布局和烧写内容modules.dep存在但modprobe仍报错rootfs只读挂载或模块目录权限问题重新以读写方式挂载rootfs确认目录权限4. 标准修复流程让buildroot重新生成完整的模块目录如果是buildroot一体构建的环境推荐走正常构建流程修复不建议在开发板上长期靠手工维护模块文件。标准流程分四步。4.1 确认内核开启模块支持执行make linux-menuconfig进入菜单后确认这个选项是开启状态General setup - [*] Enable loadable module support然后把需要动态加载的驱动无线网卡、蓝牙、USB转串口等选成M。保存退出。这一步是后续所有流程的前提CONFIG_MODULES不开启模块目录和modules.dep都不会生成。4.2 强制重建linux包这里注意不要直接执行make linux或者直接执行顶层make它们不一定能强制刷新模块安装和depmod。用make linux-rebuild这个命令会强制重建linux包重新执行模块安装和depmod。构建完成后立即检查ls -l output/target/lib/modules/*/modules.dep如果能看到modules.dep文件说明这一步已经打通。如果还是没有回头检查内核配置里的CONFIG_MODULES是否真的生效了有时候你改了配置但没保存到.configbuildroot读到的还是旧值。4.3 重新打包完整rootfs镜像linux-rebuild只是更新了output/target中间目录最终烧录用的镜像还没更新。接着执行makebuildroot会重新执行rootfs打包逻辑产出新的系统镜像。烧录之前建议先解包或挂载镜像确认一下lib/modules/版本/modules.dep确实在这一步能避免烧完才发现问题白折腾一轮。4.4 版本一致性的最终验证烧录后启动进入系统执行uname -r ls /lib/modules/确认两者一致后再执行depmod -a modprobe 你的模块名不再报错就算彻底解决。depmod -a在部分系统上可以随手跑一下它能根据当前运行的内核版本重新生成依赖文件修复一些历史遗留的元数据缺失问题。5. 快速兜底方案开发板上手工生成modules.dep如果只是想临时验证某个驱动能不能挂载不想为这事重新构建整个系统可以在开发板上手工生成modules.dep。这套方案适合快速验证但不建议作为长期依赖的手段。5.1 板子上有depmod的情况如果rootfs里带了depmodbusybox提供了depmodapplet或者启用了kmod tools直接在开发板上执行depmod -a它会扫描当前内核版本对应的模块目录生成modules.dep及其他元数据文件然后modprobe就能正常工作了。这是最快、最省事的验证路径。5.2 板子上没有depmod的交叉生成方式如果目标系统里没带depmod命令可以在构建主机上利用buildroot生成的主机工具完成。在buildroot根目录执行./output/host/sbin/depmod -a -b ./output/target 内核版本号注意内核版本号必须和开发板上uname -r的输出保持一致。执行完检查ls -l output/target/lib/modules/版本/modules.dep确认文件存在后再跑一次完整make刷新rootfs镜像重新烧录即可。这里要提醒一句output/target是buildroot的中间目录后续只要某个包触发了重建这个目录可能会被还原。所以手工在output/target里生成的modules.dep只能算是应急补丁不能作为可持续的方案。想彻底解决还是得回到第4节的构建流程让脚本自己产出正确的依赖文件。5.3 如果连depmod都用不了最后一招如果板上和主机上都没有depmod可以用还有一个临时办法直接用insmod配合模块完整路径加载insmod /lib/modules/5.10.0/kernel/drivers/net/wireless/xxx/xxx.koinsmod不读取modules.dep所以能够绕开这个错误。代价是你必须自己保证模块的依赖顺序假设模块A依赖模块B就得先加载B再加载A。模块少的时候临时用一下没问题模块一多手动维护依赖顺序就会变成一场灾难所以只适合应急不适合长期使用。6. 这个坑的其他打开方式版本后缀、外部包、全内编6.1 内核LOCALVERSION导致版本不匹配内核的release字符串不一定等于内核源码版本。buildroot在编译内核时如果配置了CONFIG_LOCALVERSION-test1内核源码的include/config/kernel.release文件里会记录类似5.10.0-test1的字符串模块安装时会以这个字符串为目录名depmod也是针对它生成的。如果你的启动环境和构建环境对不上比如bootloader加载了一个没有该后缀的Image那么内核运行时的uname -r就是5.10.0和/lib/modules/5.10.0-test1/不匹配modprobe自然找不到modules.dep。排查这类问题除了uname -r还可以用cat /proc/version或者strings命令查看内核镜像里的实际版本信息。6.2 外部驱动包只拷贝.ko不跑depmod这个我在第2.3节已经提过。在buildroot中集成第三方驱动时如果只是把编译好的.ko文件通过普通的文件拷入方式放进rootfs而没有走模块依赖体系就会产出模块存在但依赖元数据缺失的状态。正确的做法是在.mk的install步骤里补上depmod调用或者直接使用buildroot的kernel-module宏来声明这个包的模块属性让框架在构建阶段统一处理。网上很多开源项目的buildroot集成代码已经在这么做了直接拿来用之前要确认和你的buildroot版本兼容。6.3 一条偷懒且保险的思路把驱动直接编进内核如果这块驱动不需要在运行时卸载和重载也没有复杂的替换需求可以在内核配置里把对应选项从M改成*直接编进内核镜像。这样就不存在模块加载的问题modules.dep的坑自然也就绕开了。代价是内核镜像会变大而且驱动初始化阶段的错误可能会导致整个系统启动失败排错会比模块方式更麻烦。这个权衡要根据实际项目需求来做没有绝对的对错。以上是我在buildroot项目里和modules.dep搏斗几次之后沉淀下来的完整排查路径。现在再遇到这类报错我的第一反应已经不是重新编译驱动而是先跑uname -r和ls /lib/modules/做对比再决定回构建环境处理还是临时在板上补一步depmod。这套方法能帮我把定位时间控制在几分钟以内如果你手头板子的驱动模块比较多或者经常在多个内核版本之间切换建议一开始就把depmod步骤固化到构建脚本里不要等到烧录到板子上才来填坑。
阅读完成 · 觉得有帮助?