本文约1600字,承接之前的帖子Linux内核驱动源码走读之编译内核及外部驱动实操指南的下一步操作--调试hello.ko驱动的安装。
关注公众号, 即可获得与Linux相关的电子书籍以及常用开发工具,文末有文档清单。
编译完驱动后,我执行insmod hello.ko发现出现如下错误:
root@infinova-virtual-machine:/mnt/disk_sdc1/kernel_6.1/hello_drv# insmod hello.koinsmod: ERROR: could not insert module hello.ko: Invalid module format
查看dmesg信息,打印如下:

这个错误 disagrees about version of symbol module_layout 是内核模块与当前运行内核版本不兼容的典型表现。
我之前的虚拟机ubuntu18.04环境中的内核版本如下:
root@infinova-virtual-machine:/mnt/disk_sdc1/kernel_6.1/linux-6.1.161# uname -r4.15.0-213-generic
而我调试的内核源码是6.1.161, module_layout 是内核用来验证模块兼容性的核心符号。当我用 6.1.161 内核源码 编译模块时,模块会带上 6.1.161 的符号版本;但当前系统运行的是 4.15.0-213-generic内核,两者的 module_layout 符号版本不匹配,内核就会拒绝加载模块。
简单说:编译模块的内核版本 ≠ 运行的内核版本 → 导致加载失败。
解决方案有两种:
【方案一】:目标是在当前 4.15.0-213-generic 内核下运行模块(推荐新手)
这是最直接的修复方式,让编译环境和运行内核完全匹配:
将编译hello.ko的Makefile文件修改引用的内核路径,将原来直接用下载的内核源码路径改为ubuntu系统里已安装的内核路径:
#KDIR?=../linux-6.1.161KDIR?=/lib/modules/$(shell uname -r)/build
重新编译安装打印如下:

hello初始化里的打印在dmesg信息中可以看到了, 驱动加载成功。
执行rmmod,会出现如下打印:

驱动卸载也成功,初始化和释放函数打印都有调用到。
【方案二】:将目标切换到 6.1.161 内核运行模块
重要提醒:在升级内核或是虚拟机版本,一定要先将原来的虚拟机做好备份,否则一旦升级导致异常虚拟机起不来,就要重新弄虚拟机环境了。
我确实需要在 6.1.161 内核下运行模块,因为后续我要走读较新版本6.1.161的内核驱动源码,那么我需要先安装并切换到该内核:
上一篇文章中我们下载了6.1.161的内核源码编译完,make install之后打印如下:

使用uname –r查看内核版本,并没有改变,依然是4.15.0-213-generic
其中的原因是make install 只是把新内核的镜像、配置文件复制到 /boot 目录,并更新了 GRUB 启动配置。但我的系统还在运行旧的 4.15.0-213-generic 内核(ubuntu18.04),所以不重启的话,uname -r 永远不会变
从截图可以看到,make install 已经成功完成:
/boot/vmlinuz 和 /boot/initrd.img 已经被设置为指向新的 6.1.161 内核
最后也执行了 update-grub 生成了新的 GRUB 启动菜单。
注意:千万别贸贸然直接重启虚拟机,这样做可能会导致虚拟机起不来,因ubuntu18.04直接从4.15的内核到6.1的内核兼容性不好,需要做好环境准备,安装相关依赖包,否则直接升级内核后重启虚拟机可能起不来,我已踩过坑。我前天已经升级ubuntu到20.04,内核的版本现在是:5.4.0-216-generic
现在将Ubuntu 20.04 内核5.4的版本升级到6.1 内核需要的依赖包如下:
一次性安装:
# 更新系统包列表
sudo apt update && sudo apt upgrade -y# 安装编译必备依赖
sudo apt install -y build-essential libncurses5-dev libssl-dev \bc flex bison libelf-dev dwarves rsync kmod cpio initramfs-tools
关键说明: dwarves 是 6.1 内核编译必需的(提供pahole工具,用于调试信息生成),Ubuntu 20.04 默认源可直接安装;initramfs-tools 用于生成适配的 initrd 镜像。
安装完依赖包后,按照之前的文档编译6.1.161内核。
踩坑实录:我在编译6.1.161内核时用的是默认配置make defconfig,这样导致的后果是编译出来的内核,重启虚拟机依然起不来, 进入了initramfs。不过还好有补救措施,我们在重启客户机选项时选择之前的旧内核版本(5.4)启动,还是能正常进入虚拟机环境,如下图所示:


虚拟机可以正常起来后,重新编译内核,配置文件改为:
cp /boot/config-$(uname -r) .config# 更新配置:自动适配6.1内核,保留旧配置的兼容项(关键)make olddefconfig
然后编译安装驱动和内核:
make && make modules && make install_modules && make install上述操作完成后,重要的两个步骤来了(PS: 我前面未操作这两个步骤导致虚拟机启动不了):
【步骤1】强制更新initramfs(确保initrd镜像适配6.1内核)
sudo update-initramfs -c -k 6.1.161【步骤2】更新GRUB启动菜单(关键!让系统识别新内核)
sudo update-grub最后再重启虚拟机。
方案二不推荐新手使用,目前没有一款官方Ubuntu版本的内核是6.1.161版本的,如果自己调试可以下载与系统相同内核的源码来走读。如果实在想用这一款,就按照下面的原则来操作。
[1].版本选择:优先 Ubuntu 22.04,次选 20.04,不推荐 18.04,24.04 兼容但无必要;
[2].核心前提:编译依赖完整 + 内核配置适配硬件 + 驱动与内核版本匹配,这三点是驱动能直接加载的关键;
[3].新手建议:用 Ubuntu 22.04 编译 6.1.161 内核,依赖适配性最好,驱动加载成功率最高,无需额外踩坑。
温馨提示:如果只是学习内核驱动,直接找虚拟机内核版本的源码走读分析,就不需要这么折腾环境了。嵌入式的交叉编译过程,环境不同,编译的复杂度不同,喜欢折腾的多折腾,不喜欢折腾的按照最简流程操作即可。
以上为全文内容。
这里是女程序员的笔记本
15年+嵌入式软件工程师兼二胎宝妈
分享读书心得、工作经验,自我成长和生活方式。
希望我的文字能对你有所帮助