本帖最后由 正点原子运营 于 2021-11-1 10:42 编辑
以下文章摘自微信公众号——开源电子网《内核模块的version magic 你知多少?》
更多技术文章,请扫下方二维码关注
本节我们来了解内核模块的version magic,也就是kernel(内核)和内核模块的版本问题。本节的内容是笔者的一些整理和理解,如有不对的地方,希望大家指正,我们互相学习,共同进步。
1. 问题发现 相信很多小伙伴在跟着《【正点原子】I.MX6U嵌入式Linux驱动开发指南》学习Linux,尤其是刚入门Linux的小伙伴在加载驱动.ko文件的时候可能会遇见类似如下报错: - version magic '4.1.15 SMP preempt mod_unload modversions ARMv6 p2v8 ' should be '4.1.15-
复制代码图1. 1 报错信息
以上报错信息提示当前插入led.ko的version magic版本信息与正在运行的内核的version magic版本不一致,led.ko的version magic版本信息是” 4.1.15 SMP preempt mod_unload modversions ARMv6 p2v8”,kernel的version magic版本信息是” 4.1.15-gad512fa SMP preempt mod_unload modversions ARMv7 p2v8”。 出现这种问题的原因一般是kernel与内核模块没有同时更新导致版本不一样,比如新编译的模块所对应的kernel和板子上正在运行的kernel发生了变化,模块与板子上的kernel存在不兼容等,导致模块加载失败。上面的问题正是小伙伴拿到板子以后,直接使用板子烧录好的kernel而并没有使用自己编译的kernel来做驱动实验而导致的错误。
2. 如何查看内核版本和内核模块版本2.1 uname和modinfo指令用法
2.1 uname指令主要用于查询kernel版本相关信息,用法: uname [选项参数] - 选项参数如下:
- -a,依次按照如下选项输出有关信息:
- -s, 输出kernel名称;
- -n, 输出主机名;
- -r, 输出kernel发行版本号;
- -v, 输出操作系统版本;
- -m, 输出主机的硬件架构名称;
- -p, 输出处理器类型;
- -i, 输出硬件平台;
- -o, 输出操作系统名称
复制代码 一般用的较多的是uname -r和uname -a。 modinfo主要用于查看内核模块的信息,用法: modinfo 模块名
2.2 查看kernel和内核模块版本 在开发板文件系统下执行uname –r可以查看kernel发行版号,执行如下指令: 在文件系统的/lib/modules下,可以查看内核模块版本(/lib/modules/下的文件夹):
图2.2. 1 查看kernel和内核模块版本信息 可以看到,kernel和内核模块的发行版本号都是4.1.15-gad512fa。 uname –r查看的是内核发行版本号,uname –a可以查看更具体的版本信息,如下:
图2.2. 2 查看kernel具体版本信息 - 内核名称:Linux
- 主机名:ATK-IMX6U
- kernel发行版号:4.1.15-gad512fa
- 操作系统版本和时间:#1 SMP PREEMPT Mon Mar 29 16:02:02 CST 2021
- 硬件架构、处理器类型和硬件平台都是armv7l
- 操作系统名称:GNU/Linux
复制代码
当开发板启动到kernel阶段时,kernel启动时也会打印kernel的版本号: - Linux version 4.1.15-gad512fa (alientek@ubuntu) (gcc version 5.3.0 (GCC) ) #1 SMP PREEMPT Mon Mar 29 16:02:02 CST 2021
复制代码 以上信息中(alientek@ubuntu)是编译kernel所使用的机器,其中,alientek是用户名,ubuntu是主机名。(gcc version 5.3.0 (GCC) )是gcc版本。通过(alientek@ubuntu)这一串打印信息,可以初步判断板子上的kernel是否是自己的ubuntu编译出来的(因为不同用户设置的ubuntu用户名会不一样),最后一串Mon Mar 29 16:02:02 CST 2021可以显示编译kernel的时间,我们在替换kernel的时候也可以通过这个时间信息来区分此时板子上的kernel是否是我们刚替换的kernel。
图2.2. 3 开发板kernel启动的信息 2.3 查看加载模块.ko版本信息 查看所加载的模块.ko版本:
图2.3. 1 查看led.ko版本信息 - root@ATK-IMX6U:/lib/modules/4.1.15-gad512fa# modinfo led.ko
- filename:/lib/modules/4.1.15-gad512fa/led.ko
- author: zuozhongkai
- license: GPL
- srcversion: 597E1DDC8A372707B8FD0DE
- depends:
- vermagic: 4.1.15 SMP preempt mod_unload modversions ARMv6 p2v8
复制代码对以上基本信息的简单解释: - filename: 文件路径
- author: 显示模块开发人员
- license: 协议
- srcversion: 版本信息
- depends: 依赖文件
- vermagic:即 Version Magic String,也就是编译模块时的内核版本以及SMP与PREEMPT等配置信息。
复制代码 根据打印信息可以知道,在led.ko文件的版本信息中,发行版号是4.1.15,和前面kernel发行版本号4.1.15-gad512fa不一致,而且显示ARMv6,而不是ARMv7,这就会导致模块.ko文件无法加载,所以会报前文的错误。
3. kernel版本由什么控制
下面,笔者以自己移植的kernel为例进行讲解。 3.1 kernel基础版本号 在内核源码根目录的Makefile下可以看到Linux kernel的基础版本号,如下,Linux kernel基础版本号为 4.1.15:
图3.1. 1 kernel源码根目录下Makefile文件
3.2 kernel拓展版本号 kernel引入了一些配置信息来增强版本信息,在内核源码的include/linux/vermagic.h下我们可以看到模块的健全版本信息,如下默认配置的有: - MODULE_VERMAGIC_SMP=SMP;
- MODULE_VERMAGIC_PREEMPT= preempt;
- MODULE_VERMAGIC_MODULE_UNLOAD=mod_unload;
- MODULE_VERMAGIC_MODVERSIONS= modversions;
复制代码 其余选项的宏是空的,还有一个VERMAGIC_STRING宏: - #define VERMAGIC_STRING \
- UTS_RELEASE " " \
- MODULE_VERMAGIC_SMP MODULE_VERMAGIC_PREEMPT \
- MODULE_VERMAGIC_MODULE_UNLOAD MODULE_VERMAGIC_MODVERSIONS \
- MODULE_ARCH_VERMAGIC
复制代码
图3.2. 1 vermagic.h文件内容 其中,UTS_RELEASE的配置在内核源码的include/generated/utsrelease.h下可以看到, utsrelease.h的内容是由Makefile和.config的内容进行生成的,当成功编译kernel以后,utsrelease.h得到更新,如下: - #define UTS_RELEASE "4.1.15"
复制代码
图3.2. 2 utsrelease.h文件内容 那么前文4.1.15-gad512fa中的-gad512fa是如何得来的呢?其实这个是开发者使用了Git来管理代码(也有使用SVN来管理代码)。如果你在开发的时候ubuntu安装了Git,在代码维护上可以使用Git来维护,当你修改或者提交代码以后,那么在UTS_RELEASE后面就会看到一串数字,例如4.1.15-gad512fa,-gad512fa这个就是Git版本控制而产生的。 4.1.15-gad512fa这一串就组成了kernel发行版本号,发行版本号只是各个厂商用于区别自己发布的不同时期的kernel版本或者产品版本而产生的编号,完全由各厂商自己定义。 这么一来,我移植的内核模块的部分版本信息就确定了,也就是: - 4.1.15 SMP preempt mod_unload modversions
复制代码 led.ko模块在编译时,kernel的版本信息会被添加到模块的modinfo段中,所以我们可以通过modinfo led.ko来查看模块的version magic信息,在加载模块的时候,装载器会比较kernel的version magic与模块modinfo段里的version magic信息,如果它们信息一致,模块才可以被加载,否则就会报错前文的信息,或者报错找不到模块,如:“could not search modules: No such file or directory”,又或者报错“Invalid module format”。
3.3 CPU核心家族选择 此外,还注意到前面提示的ARMv7和ARMv6不一样,他们是CPU核心家族,ARM11是ARMv6架构,Cortex-A、Cortex-R、Cortex-M三个系列是ARMv7架构,我们使用的阿尔法开发板的CPU是Cortex-A7内核,所以是基于 ARMv7 的平台,ARMv7是向下兼容ARMv6的。CPU核心家族选项可以在配置内核的时候进行选择。
4. 问题解决
针对前文出现的问题,下面我给出两种解决办法。
4.1 同步更新kernel、内核模块(推荐) 使用同一个内核源码重新编译kernel、设备树和内核模块,然后将板子的kernel和设备树一起更新,并安装内核模块,这一步做好以后再去做驱动实验部分。 - <font color="#2e8b57">/* 编译内核 */</font>
- make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage
- <font color="#2e8b57">/* 编译设备树(设备树名字根据自己的实际情况来写) */</font>
- make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx6ull-alientek-emmc.dtb
- <font color="#2e8b57">/* 编译内核模块 */</font>
- make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules
- <font color="#2e8b57">/* 安装内核模块 */</font>
- make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules_install INSTALL_MOD_PATH=<font color="#9932cc">后面加上文件系统的路径</font>
复制代码
图4.2. 1 修改内核源码根目录Makefile下的EXTRAVERSION 执行如下指令重新配置内核: - make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
复制代码配置如下,在System Typeà Multiple platform selectioà ARMv6 based platforms (ARM11)下将CPU核心家族选择的ARMv6 based platforms (ARM11)选项去掉,只留下ARMv7 based platforms (Cortex-A, PJ4, Scorpion, Krait)这项:
图4.2. 2 选择CPU核心家族 然后重新编译kernel,在这个过程中,修改Makefile下的EXTRAVERSION =-gad512fa以后,重新编译kernel,最终kernel发行版本4.1.15-gad512fa信息会根据Makefile和.config的内容进行生成并记录在内核源码的include/generated/utsrelease.h下,如下是我编译kernel以后utsrelease.h文件的内容: - #define UTS_RELEASE "4.1.15-gad512fa"
复制代码
图4.2. 3 编译后查看utsrelease.h文件的内容 接着我们再编译led.ko文件,再次使用modinfo led.ko查看模块信息:
图4.2. 4 查看重新编译的led.ko的版本信息 查看led.ko版本信息是4.1.15-gad512fa SMP preempt mod_unload modversions ARMv7 p2v8,将led.ko重新拷贝到板子上(板子上的kernel不必更新),再次加载led.ko不再报错:
图4.2. 5 加载led.ko不再报错
|