ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

modprobe报错can‘t open modules.dep?嵌入式Linux下buildroot模块依赖缺失排查与修复

modprobe报错can‘t open modules.dep?嵌入式Linux下buildroot模块依赖缺失排查与修复 做嵌入式Linux开发尤其是用buildroot从零构建rootfs的时候总会碰到一些看起来莫名其妙、查起来又很耗时的报错。比如我今天要聊的这个板子启动正常串口也能进shell执行modprobe加载驱动却给我弹一句modprobe: cant open modules.dep: No such file or directory。我第一次看到这行报错是在一次内核版本升级之后当时第一反应是buildroot缺少modprobe这个命令后来才发现根本不是modprobe好好的缺的是modules.dep这个模块依赖数据库文件。这篇文章我会把这个报错涉及的机制拆开讲清buildroot里模块是怎么进rootfs的、modules.dep是怎么生成的以及你遇到这个问题时该按什么顺序排查和修复。不管是刚开始玩buildroot的新手还是已经踩过坑想找人确认思路的老手相信都能从里面找到有用的信息。1. 问题场景与根因分析1.1 这个报错到底在说什么先把报错本身的字面意思说透。modprobe这个工具和insmod有本质区别insmod是你给我哪个文件我就加载哪个文件完全不关心模块之间的依赖关系modprobe则是先读一个叫modules.dep的依赖关系数据库搞清楚模块A是否依赖模块B、模块C再按依赖顺序把所有需要的模块依次加载。这个modules.dep文件按Linux的模块加载约定应该放在/lib/modules/$(uname -r)/这个目录下面和真正的.ko文件放在一起。所以当你敲下modprobe它做的第一件事就是去/lib/modules/$(uname -r)/modules.dep找这个文件。找不到就直接报cant open modules.dep。换句话说这不是modprobe命令本体缺失而是它工作所需的分拣表丢了。我经常用一个类比来解释modprobe像一个快递分拣员modules.dep是分拣表没有分拣表分拣员完全不知道该把包裹发给谁。你可能会觉得那我自己直接指定ko文件路径用insmod不就行了可以但modules.dep里除了依赖关系还有模块别名alias和符号symbols信息很多驱动是通过设备ID匹配自动加载的没有这些文件自动加载机制就废了。这类问题典型的触发场景有几种刚烧写完一个自编译rootfs、刚切换过内核版本、或者手动往板子上拷贝过几个编译好的ko文件。系统本身看起来一切正常启动、网络、串口全部OK一执行modprobe就露馅。这正好是嵌入式开发里最难防的一类坑——表面正常底层缺件。1.2 buildroot里为什么会缺modules.dep要回答这个问题得先分清几种不同成因因为它们对应的修复方式完全不一样。第一种目标板文件系统里/lib/modules/目录根本不存在或者是个空目录。这种情况多半是构建rootfs时没有执行模块安装步骤。buildroot虽说是一套一键构建的体系但内核配置、模块配置这些环节是可以独立开关的如果某个配置项没打开模块压根不会被安装进rootfs。最后做出来的镜像里自然找不到modules.dep。第二种目录存在里面也有ko文件但唯独没有modules.dep和modules.alias这些周边文件。这种情况最常见于手动拷贝模块。比如你从别的工程里拷了一个自己编译的spi驱动ko进板子或者把某个厂商SDK里的驱动目录整个复制到了/lib/modules下。复制文件这个动作本身不会触发depmod去生成依赖关系文件于是板子上就只有一堆孤零零的komodprobe依然没法工作。第三种目录存在modules.dep也存在但你运行的内核版本和目录名对不上。buildroot编译内核时如果用的是5.10.x安装模块时目录就是/lib/modules/5.10.x/。结果板子上跑的rootfs是另一套uname -r返回5.4.ymodprobe按5.4.y去找目录发现根本没有照样报这个错。这种情况在多人协作、拿旧镜像配新内核、或者buildroot输出物混用的场景下非常常见。第四种比较隐蔽。buildroot构建流程里内核的.config中CONFIG_MODULES没有打开或者buildroot侧没有使能内核模块相关选项。内核编译时压根不生成ko文件后面modules_install和depmod这两步自然被跳过。你到output/target目录里看/lib/modules/下面干干净净好像一切正常但你根本没法用modprobe。这类问题光看报错很难想到要回buildroot配置里查所以我每次遇到modprobe相关的问题都会先怀疑构建配置再怀疑文件系统内容。把这几种成因对照一下实际报错modprobe报cant open modules.dep时它并不在乎你的ko文件在不在它只认modules.dep这个文件。所以这个报错本质上就是模块依赖数据库缺失的代名词。搞清楚这点排障方向就不会跑偏。2. buildroot中模块安装的完整链路2.1 内核模块从编译到rootfs的流转过程在buildroot里一个内核模块要最终出现在板子的/lib/modules/下经历的流程比很多人想象的要长。先从内核源码编译出.ko文件然后执行modules_install把ko文件按内核源码树中的目录结构复制到指定根目录下最后运行depmod生成依赖关系文件。buildroot把这几个步骤全部封装进了它的Kernel构建流程里对应到具体动作就是先configure、再compile、然后install modules、最后跑depmod。完成之后成果全部落在output/target/这个目录下output/target最终会被打包成rootfs.tar或ext2/squashfs等镜像文件。这里有个关键点buildroot的output/target目录就是板子上根文件系统的影子你在这个目录里看到的lib/modules/目录结构最终会原封不动地进入烧写镜像。所以排障时有个很有效的招先在构建服务器的output/target里直接检查如果这里就没有modules.dep说明是构建阶段的问题烧写多少遍都没用如果这里有、板子上没有那就要怀疑镜像烧写、分区挂载这些事情了。日常开发中我们改完内核配置后通常不会整个来一遍全量make而是用make linux-rebuild来单独重建内核。这个target会重新编译内核、重新安装模块、重新生成依赖文件然后把结果同步到output/target。用熟之后整个迭代周期能控制在几分钟以内比全量构建省太多时间。2.2 modules.dep是怎么生成的modules.dep文件由depmod工具扫描/lib/modules/$(uname -r)/目录下所有ko文件后生成。扫描时会解析每个ko文件里的符号引用判断它依赖哪些其他模块然后把依赖关系按固定格式写进modules.dep。同时还会生成modules.alias设备别名、modules.symbols符号所属模块等辅助文件。拿一段真实的modules.dep内容举例/lib/modules/5.10.0/kernel/drivers/spi/spi-dev.ko: /lib/modules/5.10.0/kernel/drivers/usb/serial/ftdi_sio.ko: /lib/modules/5.10.0/kernel/drivers/usb/serial/usbserial.ko第一行表示spi-dev.ko没有依赖任何其他模块冒号后面是空的第二行表示ftdi_sio.ko依赖usbserial.ko加载时必须先加载usbserial.ko。modprobe读取这个文件后就会按这个先后顺序来加载。如果modules.dep缺失modprobe完全没有可用的依赖信息自然直接退出。实际生成命令很简单在目标板上执行depmod -a-a表示扫描/lib/modules/下所有内核版本目录。在构建端也可以指定-b参数和版本号针对特定rootfs目录生成这在交叉编译环境里尤其有用。别忘了depmod不是内核的一部分它属于kmod工具集虽然几乎所有发行版都默认带但在裁剪过的嵌入式系统里不一定是标配。2.3 busybox modprobe和kmod modprobe的差别buildroot生成的系统里modprobe这个命令有两个来源一个是busybox自带的applet一个是由BR2_PACKAGE_KMOD引入的完整版kmod工具。很多人没意识到这两个modprobe的行为是有差异的。busybox的modprobe实现比较精简它只做最基本的依赖解析和模块加载支持的参数也少。好处是省空间、不依赖额外库坏处是遇到复杂场景容易不按常理出牌诊断信息也不够详细。kmod的modprobe则跟桌面Linux发行版上用的完全是同一个项目功能齐全报错信息更人性化比如模块格式错误、vermagic不匹配这些它都能给出相对明确的提示。我建议在开发阶段如果空间允许直接在buildroot里打开kmod包这能省掉很多莫名其妙的调试时间。特别是在排查模块加载问题时kmod modprobe输出的错误往往直接指向根因而busybox版可能只会回一句not found让你无从下手。这一点在嵌入式开发里属于性价比很高的一个选择。3. 排查与修复实操步骤3.1 先看目标板的目录结构到了板子上别急着重新烧镜像先按顺序跑几条命令把现状摸清楚。uname -r ls -la /lib/modules/ ls -la /lib/modules/$(uname -r)/ find /lib/modules/ -name *.ko | head -20第一条看当前运行内核版本第二条看系统里有哪些模块目录第三条看当前版本目录里的具体内容第四条确认ko文件到底有没有。这三条命令一跑绝大多数情况就能定性了。我遇到过最典型的情况是uname -r返回5.15.0但/lib/modules/下面只有5.10.0一个目录。这种情况不用想就是内核和rootfs不是同一套来源可能烧错了镜像也可能是构建时内核版本配置不一致。另外也有一种情况是目录名和版本对上了但目录里只有ko文件没有任何modules.dep相关文件这说明构建流程漏了depmod步骤或者有人手动往系统里拷了模块但没跑depmod。如果/lib/modules/整个目录都不存在问题更直接基本上就是构建时模块安装没执行需要回到buildroot配置里查。3.2 buildroot侧配置排查与重新构建在构建服务器上先用menuconfig检查配置make menuconfig进入Kernel配置菜单确认Linux Kernel相关选项已经使能再检查内核自身的.config文件确认CONFIG_MODULESy。这个选项如果没打开后面所有模块相关操作都是空谈。在buildroot的Kernel配置项里还要确认和模块安装相关的开关是打开状态。不同buildroot版本界面文案略有差异但逻辑一致模块只有被勾选安装才会在modules_install阶段被复制到output/target。确认配置无误后重新构建内核相关部分make linux-rebuild makemake linux-rebuild会重新运行内核的编译并重复模块安装、depmod的流程后面的make则保证rootfs整体打包时把最新内容打进去。构建完成后直接检查output/target/lib/modules/目录ls -la output/target/lib/modules/$(grep -m1 ^BR2_LINUX_KERNEL_CUSTOM_VERSION .config 2/dev/null | cut -d -f2 2/dev/null)/如果这里能看到modules.dep说明构建端已经正常剩下就是重新生成rootfs镜像并烧写。3.3 快速修复手动生成modules.dep有些场景下不想重新烧整个镜像或者手头没有构建环境只有一根串口线和一个shell那可以尝试在板端直接生成缺失的依赖文件。先把根文件系统重新挂成可写mount -o remount,rw /然后执行depmoddepmod -a sync这里有一个前提板子上必须有depmod这个命令。如果是busybox系统depmod未必被编译进去如果没有可以临时拷贝一个静态编译的depmod二进制上板或者干脆用kmod。如果板子有网络和包管理系统直接装kmod包也行。另外还有一种更正规的做法从构建端处理在buildroot根目录里用交叉编译环境自带或kmod编译生成的depmod工具直接针对rootfs目录生成依赖文件output/host/bin/depmod -b output/target 5.10.0这条命令的意思是把output/target当作根文件系统根目录扫描其中lib/modules/5.10.0/下的所有模块生成依赖文件并写回。生成的modules.dep会出现在output/target/lib/modules/5.10.0/modules.dep之后重新打包rootfs镜像烧写即可。这个方式的优势是不依赖板端环境适合批量处理多个同构板子。3.4 复现验证与自启动配置修复完成之后不能只看modprobe不报错就认为完事得完整验证一遍模块确实加载成功。modprobe module_name lsmod dmesg | tail -50modprobe无输出通常只是第一步lsmod可以看到模块是否真的在内存里dmesg则能看到驱动初始化过程中有没有打印错误。很多时候modprobe加载模块本身没报错但驱动probe失败问题出在硬件初始化环节那是另一个层面的问题。验证时别漏了这一层。如果把模块做成开机自动加载buildroot里可以往/etc/init.d/放一个启动脚本也可以用target/overlay机制往rootfs里预置内容。我常用的做法是建一个board/我的板子/rootfs-overlay/etc/init.d/S99modules脚本内容很简单#!/bin/sh case $1 in start) echo Loading kernel modules... modprobe spi-dev modprobe ftdi_sio ;; stop) rmmod spi-dev rmmod ftdi_sio ;; esac在buildroot配置里指定overlay目录路径重新make后这个脚本就会自动进入镜像。比每次烧写后手动敲命令要省心得多。4. 常见问题与独家经验4.1 这类报错的衍生问题速查表在开发过程中围绕modprobe和模块加载我还遇到过不少连带问题整理成一张速查表方便你遇到类似情况时快速定位现象可能原因常规处理cant open modules.dep依赖数据库缺失检查目录结构重新depmod或重新构建module xxx not found模块名/路径不匹配别名缺失find查找ko文件位置用modinfo核对名称Exec format errorko架构或内核版本与运行内核不一致用当前内核源码重新编译模块Invalid module formatvermagic不匹配检查uname -r和模块编译版本需重编Operation not permitted核内安全模块拦截或权限问题检查kernel lockdown、selinux等设置这表里的前两类和今天说的modules.dep问题经常一起出现后三类则更多是模块编译环境配置导致。排查的时候我建议顺序固定先看版本、再看目录、再看文件、最后看权限。按这个顺序来基本都能在十分钟内给出结论。4.2 手动拷模块的翻车现场我曾经为了图省事直接把构建服务器上output/target/lib/modules/里的某个厂商WiFi驱动ko整个目录拷到板子上想省去重新烧镜像的麻烦。结果modprobe报错不说操作系统日志里还多了一串firmware加载失败的信息。查到最后发现这个驱动不光要ko文件还依赖一套firmware二进制firmware没拷对模块即使加载起来也跑不起来。从那以后我就学乖了手动拷模块不是不能做但必须先弄清楚模块的依赖、固件路径、vermagic这些附加条件最好用modinfo查看一下模块信息。另一个常见的坑是权限问题。嵌入式系统的/lib/modules目录通常是root用户的普通用户执行modprobe时如果没有正确配置udev或者权限也可能出现类似Operation not permitted的报错虽然这个和modules.dep缺失不是同一个原因但排障时容易混淆。我习惯在确认版本和目录都正常后顺手用id看一下当前用户别让权限问题干扰判断。4.3 我在开发中沉淀的几条模块管理习惯最后分享几个我自己的实践习惯都是我踩过坑之后沉淀下来的。第一模块尽量走buildroot流程不要手工往镜像里塞ko文件。这不是说手工操作一定不行而是手工操作容易漏掉依赖、漏掉版本匹配、漏掉depmod这几步。把模块添加变成buildroot的一部分整个流程可重复、可追溯对多人协作尤其重要。第二每次构建完习惯性检查output/target/lib/modules下的内容确认modules.dep存在、内核版本目录对得上。这个检查只需要十秒钟但能避免烧写完镜像才发现问题的尴尬。我现在基本上已经形成肌肉记忆了。第三用make linux-rebuild进行迭代。改一个驱动模块或者调内核配置可以快速验证不用动不动全量make。但要注意如果是改了buildroot的packages配置还是要用完整make来保证依赖关系不会错乱。第四对于复杂的模块依赖直接用modinfo在目标板上查模块信息modinfo module_namemodinfo会输出模块的文件路径、vermagic、依赖模块列表等关键信息能帮你快速判断模块是否适用于当前内核。最后说点我自己的体会吧。这个报错本身不复杂但它背后涉及的是整个嵌入式Linux模块加载机制的运作逻辑。modprobe依赖modules.dep而modules.dep又来源于构建流程中的depmod步骤任意一环断开用户端呈现的就是一行冷冰冰的No such file or directory。我后来养成了一个习惯每次构建完rootfs先别急着烧先在output/target/lib/modules/下面看一眼确认该有的文件都在。这个习惯帮我省了很多次返工。如果你也被这个报错卡过希望这篇文章能帮你少走一些弯路。
返回列表