侧边栏壁纸
  • 累计撰写 14 篇文章
  • 累计创建 37 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Linux 内核升级后驱动突然失效?一次 sio_gpio “Module not found” 排查记录

猿有味
2026-05-21 / 0 评论 / 1 点赞 / 6 阅读 / 0 字

摘要

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

这次报错是:

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) 目录下。

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

一、问题现象

现场执行:

sudo modprobe 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 | grep vermagic

如果看到类似:

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 只能当现场尝试,别把它当成正式修复。

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

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

sudo mkdir -p /lib/modules/$(uname -r)/extra

sudo cp /lib/modules/6.8.0-107-generic/extra/sio_gpio.ko \
  /lib/modules/$(uname -r)/extra/

sudo depmod -a "$(uname -r)"
sudo modprobe sio_gpio

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

sudo depmod -a "$(uname -r)"

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

加载后再确认:

lsmod | grep sio_gpio

如果还想看内核侧日志:

sudo dmesg | tail -50

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

五、如果提示 Invalid module format

如果复制后执行:

sudo modprobe sio_gpio

报:

Invalid module format

或者 dmesg 里出现类似:

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

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

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

先装当前内核头文件:

sudo apt update
sudo apt install build-essential "linux-headers-$(uname -r)"

然后进入 sio_gpio 源码目录:

make clean
make

安装到当前内核目录:

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

最后再确认:

lsmod | grep sio_gpio

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

六、排查顺序

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

flowchart TD A[modprobe sio_gpio 失败] --> B[查看当前内核 uname -r] B --> C[查看 /lib/modules/当前内核/extra] C --> D{当前内核目录下是否有 sio_gpio.ko} D -- 否 --> E[从旧目录复制到当前内核目录] E --> F[执行 depmod -a 当前内核版本] F --> G[modprobe sio_gpio] D -- 是 --> G G --> H{是否加载成功} H -- 是 --> I[临时恢复完成] H -- 否 --> J[查看 dmesg] J --> K{是否 Invalid module format} K -- 是 --> L[按当前内核重新编译模块] K -- 否 --> M[继续查依赖、符号、权限和路径]

这张图里别跳过前两步:

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

编辑:

sudo vim /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:

sudo update-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 内核元包,可以锁定:

sudo apt-mark hold linux-generic linux-image-generic linux-headers-generic

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

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

sudo apt-mark hold linux-image-6.8.0-107-generic
sudo apt-mark hold linux-headers-6.8.0-107-generic
sudo apt-mark hold linux-modules-6.8.0-107-generic
sudo apt-mark hold linux-modules-extra-6.8.0-107-generic

查看已经锁定的包:

apt-mark showhold | grep linux

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

九、长期还是上 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)"

sudo mkdir -p /lib/modules/$(uname -r)/extra

sudo cp /lib/modules/6.8.0-107-generic/extra/sio_gpio.ko \
  /lib/modules/$(uname -r)/extra/

sudo depmod -a "$(uname -r)"
sudo modprobe sio_gpio

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

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

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

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

参考

1

评论区