显然,我们强烈建议您重新编译内核,这样您就可以启用许多有用的调试功能,例如强制模块卸载 (MODULE_FORCE_UNLOAD):启用此选项后,即使内核认为卸载模块不安全,您也可以通过 rmmod -f module 命令强制内核卸载模块。此选项可以为您节省大量时间和模块开发过程中的多次重启。
然而,在许多情况下,您可能希望将模块加载到预编译的运行内核中,例如常见 Linux 发行版附带的内核,或者您过去编译的内核。在某些情况下,您可能需要编译模块并将其插入到您不允许重新编译的运行内核中,或者插入到您不想重启的机器上。如果您想不出任何情况会迫使您为预编译内核使用模块,您可以跳过此部分,并将本章的其余部分视为一个大的脚注。
现在,如果您只是安装一个内核源代码树,使用它来编译您的内核模块,并尝试将您的模块插入内核,在大多数情况下,您会收到如下错误
insmod: error inserting 'poet_atkm.ko': -1 Invalid module format |
不太隐晦的信息已记录到/var/log/messages:
Jun 4 22:07:54 localhost kernel: poet_atkm: version magic '2.6.5-1.358custom 686 REGPARM 4KSTACKS gcc-3.3' should be '2.6.5-1.358 686 REGPARM 4KSTACKS gcc-3.3' |
换句话说,您的内核拒绝接受您的模块,因为版本字符串(更准确地说,是版本魔数)不匹配。顺便说一下,版本魔数以静态字符串的形式存储在模块对象中,以vermagic开头。版本数据在您的模块链接到init/vermagic.o文件时插入到模块中。要检查给定模块中存储的版本魔数和其他字符串,请执行 modinfo module.ko 命令
[root@pcsenonsrv 02-HelloWorld]# modinfo hello-4.ko license: GPL author: Peter Jay Salzman <p@dirac.org> description: A sample driver vermagic: 2.6.5-1.358 686 REGPARM 4KSTACKS gcc-3.3 depends: |
为了克服这个问题,我们可以求助于 --force-vermagic 选项,但此解决方案可能不安全,并且在生产模块中绝对不可接受。因此,我们希望在一个与构建预编译内核的环境相同的环境中编译我们的模块。如何做到这一点,是本章余下部分的主题。
首先,确保有一个内核源代码树可用,其版本与当前内核完全相同。然后,找到用于编译预编译内核的配置文件。通常,它在您当前的/boot目录下,名称类似于config-2.6.x。您可能只想将其复制到您的内核源代码树: cp /boot/config-`uname -r` /usr/src/linux-`uname -r`/.config。
让我们再次关注之前的错误消息:仔细查看版本魔数字符串表明,即使使用两个完全相同的配置文件,版本魔数也可能存在细微差异,这足以阻止模块插入到内核中。这种细微的差异,即custom字符串,它出现在模块的版本魔数中,但没有出现在内核的版本魔数中,是由于某些发行版包含的 makefile 中的修改造成的。然后,检查您的/usr/src/linux/Makefile,并确保指定的版本信息与当前内核使用的版本信息完全匹配。例如,您的 makefile 可能像这样开始
VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 5 EXTRAVERSION = -1.358custom ... |
在这种情况下,您需要将符号EXTRAVERSION的值恢复为-1.358。我们建议保留一份用于编译内核的 makefile 备份副本,存放在/lib/modules/2.6.5-1.358/build中。一个简单的 cp /lib/modules/`uname -r`/build/Makefile /usr/src/linux-`uname -r` 应该足够了。此外,如果您已经使用之前的(错误的)Makefile开始了内核构建,您还应该重新运行 make,或者直接修改文件UTS_RELEASE中的符号/usr/src/linux-2.6.x/include/linux/version.h,根据文件/lib/modules/2.6.x/build/include/linux/version.h的内容,或者用第一个文件覆盖后一个文件。
现在,请运行 make 以更新配置和版本头文件及对象
[root@pcsenonsrv linux-2.6.x]# make CHK include/linux/version.h UPD include/linux/version.h SYMLINK include/asm -> include/asm-i386 SPLIT include/linux/autoconf.h -> include/config/* HOSTCC scripts/basic/fixdep HOSTCC scripts/basic/split-include HOSTCC scripts/basic/docproc HOSTCC scripts/conmakehash HOSTCC scripts/kallsyms CC scripts/empty.o ... |
如果您不想实际编译内核,您可以在 CTRL-C 之后立即中断构建过程 (SPLIT行),因为到那时,您需要的文件将已准备就绪。现在您可以返回到模块目录并编译它:它将根据您当前的内核设置进行构建,并且可以加载到内核中而不会出现任何错误。