Linux 内核升级后驱动突然失效?一次 sio_gpio “Module not found” 排查记录
2026/6/13 12:11:07 网站建设 项目流程

摘要

现场设备升级或重启后,自定义内核模块突然加载失败,这种事并不少见。

这次报错是:

Module sio_gpio not found in directory /lib/modules/6.8.0-111-generic

第一眼看,很容易以为sio_gpio.ko被删了。实际查下来,文件还在,只是放在旧内核目录里。

这次机器上旧模块放在:

/lib/modules/6.8.0-107-generic/extra/sio_gpio.ko

但系统当前已经启动到:

6.8.0-111-generic

modprobe不会满机器乱找,它默认只查当前运行内核的模块目录:

/lib/modules/$(uname-r)

所以这次要确认的不是“机器上到底有没有这个.ko文件”,而是:

这个模块是不是在当前运行内核对应的/lib/modules/$(uname -r)目录下。

下面按现场排查顺序记录一下:先怎么恢复,再怎么避免下次重启又踩一次。

一、问题现象

现场执行:

sudomodprobe sio_gpio

系统提示:

Module sio_gpio not found in directory /lib/modules/6.8.0-111-generic

/lib/modules,机器上其实有好几个内核版本:

6.5.0-18-generic 6.8.0-90-generic 6.8.0-94-generic 6.8.0-107-generic 6.8.0-110-generic 6.8.0-111-generic

再去旧内核目录看:

ls/lib/modules/6.8.0-107-generic/extra

能看到:

sio_gpio.ko

到这里就能先排除一件事:模块文件不是完全丢了。

问题卡在另一边:系统现在跑的已经不是6.8.0-107-generic,而是6.8.0-111-generic

二、先看当前内核

排这种问题,先别急着复制文件,第一条命令看当前内核:

uname-r

如果输出是:

6.8.0-111-generic

modprobe sio_gpio默认只会去这里找:

/lib/modules/6.8.0-111-generic/

它不会因为旧目录里存在:

/lib/modules/6.8.0-107-generic/extra/sio_gpio.ko

就自动跨版本加载。

所以这句Module not found不能直接理解成“整个系统里没有这个文件”。放到这次现场,它的意思更接近:

当前运行内核对应的模块目录里没有这个模块。

三、为什么换个内核就不行

Linux 内核模块不是普通动态库,不能只看文件名一样不一样。

.ko模块通常跟内核版本、内核配置、符号表和编译参数绑在一起。哪怕都是6.8.0系列,6.8.0-107-generic6.8.0-111-generic对模块来说也不是同一个目标。

可以看一下这个模块当时是按哪个内核编出来的:

modinfo /lib/modules/6.8.0-107-generic/extra/sio_gpio.ko|grepvermagic

如果看到类似:

vermagic: 6.8.0-107-generic SMP preempt mod_unload modversions

而当前:

uname-r

输出是:

6.8.0-111-generic

那就很明确了:这个模块原本是给6.8.0-107-generic编译的。

这时有两种情况:

  • 模块刚好还能被当前内核接受,复制到当前目录后可以临时加载
  • 模块和当前内核 ABI 不匹配,复制过去也会报Invalid module format

所以复制.ko只能当现场尝试,别把它当成正式修复。

四、先恢复现场:复制到当前内核目录

如果当前目标是先让设备恢复起来,可以先把旧目录里的模块放到当前内核目录。

sudomkdir-p/lib/modules/$(uname-r)/extrasudocp/lib/modules/6.8.0-107-generic/extra/sio_gpio.ko\/lib/modules/$(uname-r)/extra/sudodepmod-a"$(uname-r)"sudomodprobe sio_gpio

这里最容易漏的是这一步:

sudodepmod-a"$(uname-r)"

depmod会重新生成模块依赖索引。否则文件虽然已经复制过去,modprobe仍然可能查不到。

加载后再确认:

lsmod|grepsio_gpio

如果还想看内核侧日志:

sudodmesg|tail-50

这套操作只能算临时恢复。它解决的是“当前内核目录里没有模块”的问题,不保证模块一定适配当前内核。

五、如果提示 Invalid module format

如果复制后执行:

sudomodprobe sio_gpio

报:

Invalid module format

或者dmesg里出现类似:

version magic '6.8.0-107-generic ...' should be '6.8.0-111-generic ...'

这就说明模块和当前内核对不上。

这时继续复制就没意义了,要在当前内核下重新编译。

先装当前内核头文件:

sudoaptupdatesudoaptinstallbuild-essential"linux-headers-$(uname-r)"

然后进入sio_gpio源码目录:

makecleanmake

安装到当前内核目录:

sudomkdir-p/lib/modules/$(uname-r)/extrasudocpsio_gpio.ko /lib/modules/$(uname-r)/extra/sudodepmod-a"$(uname-r)"sudomodprobe sio_gpio

最后再确认:

lsmod|grepsio_gpio

这一步才算是针对当前内核处理。

六、排查顺序

这类问题按下面顺序走就行,没必要一上来就重装系统,也别先怀疑驱动源码。

modprobe sio_gpio 失败

查看当前内核 uname -r

查看 /lib/modules/当前内核/extra

当前内核目录下是否有 sio_gpio.ko

从旧目录复制到当前内核目录

执行 depmod -a 当前内核版本

modprobe sio_gpio

是否加载成功

临时恢复完成

查看 dmesg

是否 Invalid module format

按当前内核重新编译模块

继续查依赖、符号、权限和路径

这张图里别跳过前两步:

uname-r

和:

ls/lib/modules/$(uname-r)/extra

先把“当前内核”和“模块所在目录”对上,后面的判断才不会跑偏。

七、现场设备别让内核随便变

这次问题说到底,是系统升级后启动到了新内核。

如果这台机器是机器人主机、工控机、采集设备,或者任何依赖自定义驱动的现场设备,内核版本就别让它随便变。

可以把 GRUB 默认启动项固定到已验证过的内核,比如:

6.8.0-107-generic

先查看 GRUB 菜单项:

grep-E"^[[:space:]]*(submenu|menuentry) '"/boot/grub/grub.cfg

找到类似:

submenu 'Advanced options for Ubuntu' ... Ubuntu, with Linux 6.8.0-107-generic

编辑:

sudovim/etc/default/grub

把:

GRUB_DEFAULT=0

改成类似:

GRUB_DEFAULT="Advanced options for Ubuntu>Ubuntu, with Linux 6.8.0-107-generic"

这里的名称必须和/boot/grub/grub.cfg里的submenumenuentry名称一致,中间用>连接。

更新 GRUB:

sudoupdate-grub

重启后确认:

uname-r

如果输出:

6.8.0-107-generic

说明启动内核已经回到指定版本。

八、还要控制 apt 升级内核

只固定 GRUB 还不够,这个坑下次还可能被apt upgrade带回来。

只要后面继续执行apt upgrade,系统仍然可能安装新的内核包。现场设备如果没有驱动重编译流程,下次重启还是可能回到同一个问题。

先看本机实际安装的内核相关包:

dpkg-l'linux-generic*''linux-image*''linux-headers*''linux-modules*'|grep'^ii'

如果确认用的是 Ubuntu 普通 generic 内核元包,可以锁定:

sudoapt-mark hold linux-generic linux-image-generic linux-headers-generic

如果用的是 HWE 内核,元包名称可能是linux-generic-hwe-*linux-image-generic-hwe-*这一类,不要照抄,要按上一步查到的实际包名替换。

如果要锁定具体内核版本,也可以执行:

sudoapt-mark hold linux-image-6.8.0-107-genericsudoapt-mark hold linux-headers-6.8.0-107-genericsudoapt-mark hold linux-modules-6.8.0-107-genericsudoapt-mark hold linux-modules-extra-6.8.0-107-generic

查看已经锁定的包:

apt-mark showhold|greplinux

这个做法能稳住现场。代价也很直接:内核安全更新不会再自动跟着走,后面要有人安排升级窗口和验证流程。

九、长期还是上 DKMS

如果sio_gpio要长期用,别一直靠手动复制.ko,也别指望永远不升级内核。把它纳入 DKMS 更省心。

DKMS 干的就是这件事:

安装新内核时,自动为新内核重新编译并安装模块。

也就是说,系统从:

6.8.0-107-generic

升级到:

6.8.0-111-generic

DKMS 可以把sio_gpio重新编译到新内核对应的目录里。

我一般按这个顺序处理:

DKMS 自动重编译 > 固定内核版本 > 手动复制 .ko

手动复制是救急,固定内核是稳住现场,DKMS 才是长期维护自定义驱动该走的路。

小结

这次sio_gpio加载失败,不是模块文件被删了,而是模块还在旧内核目录,系统却已经启动到了新内核。

这几个对应关系别看错:

旧模块位置: /lib/modules/6.8.0-107-generic/extra/sio_gpio.ko 当前运行内核: 6.8.0-111-generic modprobe 查找目录: /lib/modules/6.8.0-111-generic

现场先恢复,可以复制到当前内核目录并执行depmod -a "$(uname -r)"

sudomkdir-p/lib/modules/$(uname-r)/extrasudocp/lib/modules/6.8.0-107-generic/extra/sio_gpio.ko\/lib/modules/$(uname-r)/extra/sudodepmod-a"$(uname-r)"sudomodprobe sio_gpio

如果提示Invalid module format,就别继续绕了,直接按当前内核重新编译。

以后遇到类似问题,先记住这个判断:

modprobe找的是当前运行内核的模块目录,不是以前放过模块的目录。

对工控机、机器人主机、采集卡、GPIO、串口、CAN 这类依赖内核模块的设备来说,内核升级一定要和驱动验证放在一起做。普通升级看着没什么,落到现场设备上,可能就是驱动直接起不来。

参考

  • modprobe(8) - Linux manual page
  • depmod(8) - Linux manual page
  • modinfo(8) - Linux manual page
  • Ubuntu Manpage:apt-mark(8)
  • Ubuntu Community Help Wiki:Grub2/Submenus
  • Ubuntu Community Help Wiki:Grub2/Setup
  • Ubuntu Community Help Wiki:DKMS
  • Ubuntu Wiki:Kernel/Dev/DKMSPackaging

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询