In continuation of the previous text:第二章:模块的编译与运行-7 Loading and Unloading Modules, let's GO ahead .
Version Dependency
Bear in mind that your module’s code has to be recompiled for each version of the kernel that it is linked to—at least, in the absence of modversions, not covered here as they are more for distribution makers than developers. Modules are strongly tied to the data structures and function prototypes defined in a particular kernel version; the interface seen by a module can change significantly from one kernel version to the next. This is especially true of development kernels, of course.
需要记住,你的模块代码必须针对它要链接的每个内核版本重新编译 —— 至少在没有使用模块版本控制(modversions)的情况下是如此。此处不讨论模块版本控制,因为它更多是为发行版制作者设计的,而非开发者。模块与特定内核版本中定义的数据结构和函数原型紧密绑定,不同内核版本向模块提供的接口可能会发生显著变化,开发版内核尤其如此。
The kernel does not just assume that a given module has been built against the proper kernel version. One of the steps in the build process is to link your module against a file (called vermagic.o) from the current kernel tree; this object contains a fair amount of information about the kernel the module was built for, including the target kernel version, compiler version, and the settings of a number of important configuration variables. When an attempt is made to load a module, this information can be tested for compatibility with the running kernel. If things don’t match, the module is not loaded; instead, you see something like:
内核不会默认假定某个模块是针对正确的内核版本构建的。构建过程中有一个步骤是将你的模块与当前内核源码树中的 vermagic.o 文件进行链接,该目标文件包含大量关于模块构建目标内核的信息,包括目标内核版本、编译器版本以及多个重要配置变量的设置。当尝试加载模块时,内核会验证这些信息与运行中内核的兼容性。若不匹配,模块将无法加载,你会看到类似如下的错误:
# insmod hello.ko
Error inserting './hello.ko': -1 Invalid module format
A look in the system log file (/var/log/messages or whatever your system is configured to use) will reveal the specific problem that caused the module to fail to load.
查看系统日志文件(如 /var/log/messages 或系统配置的其他日志文件),可找到导致模块加载失败的具体问题。
If you need to compile a module for a specific kernel version, you will need to use the build system and source tree for that particular version. A simple change to the KERNELDIR variable in the example makefile shown previously does the trick.
若需为特定内核版本编译模块,你需要使用该版本对应的构建系统和源码树。只需修改前文示例 Makefile 中的 KERNELDIR 变量,即可实现这一目标。
Kernel interfaces often change between releases. If you are writing a module that is intended to work with multiple versions of the kernel (especially if it must work across major releases), you likely have to make use of macros and #ifdef constructs to make your code build properly. This edition of this book only concerns itself with one major version of the kernel, so you do not often see version tests in our example code. But the need for them does occasionally arise. In such cases, you want to make use of the definitions found in linux/version.h. This header file, automatically included by linux/module.h, defines the following macros:
内核接口常在版本间发生变化。若你编写的模块需适配多个内核版本(尤其是跨主版本适配),可能需要使用宏和 #ifdef 结构确保代码正确编译。本书此版本仅聚焦于一个内核主版本,因此示例代码中不常出现版本检测,但偶尔仍有必要。这种情况下,需使用 linux/version.h 中定义的宏,该头文件会被 linux/module.h 自动包含,主要定义了以下宏:
UTS_RELEASE
This macro expands to a string describing the version of this kernel tree. For example, "2.6.10".
展开为描述当前内核树版本的字符串,例如 "2.6.10"。
LINUX_VERSION_CODE
This macro expands to the binary representation of the kernel version, one byte for each part of the version release number. For example, the code for 2.6.10 is 132618 (i.e., 0x02060a).* With this information, you can (almost) easily determine what version of the kernel you are dealing with.
展开为内核版本的二进制表示形式,版本号的每一部分各占一个字节。例如,2.6.10 对应的代码为 132618(即 0x02060a)。借助该宏,你几乎可以轻松判断当前处理的内核版本。
KERNEL_VERSION(major,minor,release)
This is the macro used to build an integer version code from the individual numbers that build up a version number. For example, KERNEL_VERSION(2,6,10) expands to 132618. This macro is very useful when you need to compare the current version and a known checkpoint.
用于将版本号的各个部分(主版本、次版本、发行版)组合成一个整数版本代码。例如,KERNEL_VERSION(2,6,10) 展开为 132618。当需要比较当前版本与已知版本节点时,该宏非常有用。
Most dependencies based on the kernel version can be worked around with preprocessor conditionals by exploiting KERNEL_VERSION and LINUX_VERSION_CODE. Version dependency should, however, not clutter driver code with hairy #ifdef conditionals; the best way to deal with incompatibilities is by confining them to a specific header file. As a general rule, code which is explicitly version (or platform) dependent should be hidden behind a low-level macro or function. High-level code can then just call those functions without concern for the low-level details. Code written in this way tends to be easier to read and more robust.
大多数基于内核版本的依赖问题,都可通过用 KERNEL_VERSION 和 LINUX_VERSION_CODE 结合预处理条件语句解决。但版本依赖不应让驱动代码充斥复杂的 #ifdef 条件判断,处理不兼容性的最佳方式是将其限定在特定头文件中。通常规则是,明确依赖版本(或平台)的代码应隐藏在底层宏或函数之后,高层代码只需调用这些函数,无需关注底层细节。采用这种方式编写的代码往往更易读、更健壮。
补充说明:
-
vermagic.o的兼容性校验逻辑:内核加载模块时,会对比vermagic.o中的内核版本、编译器版本、CONFIG_*配置(如CONFIG_SMP、CONFIG_PREEMPT),只要有一项不匹配,就判定为 “无效模块格式”,这是为了防止模块因接口不兼容破坏内核稳定性。 -
跨版本适配的代码示例:若模块需在 2.6.32 及以上版本使用新接口,可编写如下条件编译代码:
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32) new_kernel_api(); // 新内核接口 #else old_kernel_api(); // 旧内核接口 #endif -
日志查看工具:除了直接查看
/var/log/messages,还可通过dmesg命令快速查看内核最新日志,模块加载失败的具体原因(如 “version magic mismatch”)会直接显示在dmesg输出中。

被折叠的 条评论
为什么被折叠?



