编译内核模块
有时您可能希望在不重新编译整个内核的情况下编译 Linux 的 内核模块。
构建环境
首先,您需要安装构建依赖项,例如编译器(base-devel)和 linux-headers。
接下来,您需要获取模块 intended to run on 的内核版本的源代码。您可以尝试使用较新的内核源代码,但编译出的模块很可能无法加载。
如果 intended kernel version 是当前已安装的内核,请使用以下命令查找其版本:
$ uname -r
获取所需源代码主要有两种方式。每种方式在用法和目录结构上略有不同。
传统编译
请参阅 Kernel/Traditional compilation#Download the kernel source。如果您使用 Git 获取最新源代码,则需要使用标签(例如 v4.1)来检出所需版本。
Arch 构建系统
有关 Arch 构建系统的概述,请阅读 ABS。有关获取内核源代码、目录结构和其他详细信息,请参阅 Kernel/Arch build system。
源代码配置
获得源代码后,进入其目录。对于 #Arch 构建系统 的情况,该目录是 PKGBUILD 所在位置下的 src/archlinux-linux/。
make help 的输出在这里很有帮助。首先使用以下命令进行清理:
$ make mrproper
.config 和 .config.old。您可能需要先将这些文件保存到其他地方再进行清理。现在需要一个合适的 .config 文件。如果附近没有,也许是从保存的 .config 文件中获取,并且 intended kernel version 是正在运行的内核,您可以使用它的配置文件:
$ zcat /proc/config.gz > .config
接下来,请确保 .config 文件已根据内核版本进行调整。如果您使用的是确切的当前版本的内核源代码,它应该不会询问任何问题。但对于比当前内核不同的版本,系统可能会询问一些选项。无论如何,对于 #Arch 构建系统 选项,您可能需要检查 PKGBUILD::prepare() 函数。
如果您要编译的模块有一些编译选项,例如调试构建,或者它以前没有被编译过,您也可以(可能必须)调整内核配置。您可以通过 `make help` 中提到的许多配置目标之一来完成此操作。
$ make oldconfig
模块编译
为了干净地编译和加载我们的模块,我们必须找到当前内核版本号的 EXTRAVERSION 组件的值,以便我们在内核源代码中精确匹配版本号。EXTRAVERSION 是内核顶层 Makefile 中设置的一个变量,但在 vanilla 内核源代码中的 Makefile 中 EXTRAVERSION 是空的;它仅作为 Arch 内核构建过程的一部分进行设置。如果相关,可以通过查看 uname -r 命令的输出来找到当前内核的 EXTRAVERSION 值。通常,内核版本是三个组件的串联。即,数字版本、EXTRAVERSION 和 LOCALVERSION。数字版本本身是三个数字的串联。如果通过 PKGBUILD 文件构建,LOCALVERSION 将从 pkgrel 变量中获取,并带有连字符前缀。EXTRAVERSION 将是 pkgver 变量的后缀,其中数字版本第三个数字右边的句点字符将被连字符替换。例如,对于 linux 包 linux 5.5.8.arch1-1,LOCALVERSION 是 -1。EXTRAVERSION 是 -arch1。在这种情况下,uname -r 的输出将是 5.5.8-arch1-1。
一旦知道了 EXTRAVERSION 值,我们就可以准备源代码进行模块编译了:
$ make EXTRAVERSION=<YOUR EXTRAVERSION HERE> modules_prepare
示例
$ make EXTRAVERSION=-arch1 modules_prepare
或者,如果您乐意使用 --force-vermagic 选项通过 modprobe 加载模块,以忽略内核版本号的不匹配,您可以直接运行:
$ make modules_prepare
+ 字符。例如:5.5.8-arch1-1+。最后,通过指定模块的目录名称来编译想要的模块。您可以使用 modinfo 或 find 来找到模块的位置,从而也找到其目录名称。
$ make M=fs/btrfs
作为最后的手段,如果其他方法都无效,您可以:
$ make modules
这将从内核配置中构建所有模块。
树外模块编译
获取当前运行的 Linux 内核的官方源代码,如 Kernel/Arch build system 中所述。
$ cd && mkdir build $ pkgctl repo clone linux
然后,在编译模块时指向已检出的源代码:
$ cd build/mymod $ make -C ~/build/linux/src/archlinux-linux M=$PWD modules
模块安装
现在,成功编译后,您只需将其 gzip 并复制到您的当前内核即可。
如果您要替换某个现有模块,您将需要覆盖它(并记住重新安装 linux 会将其替换为默认模块)。
$ zstd fs/btrfs/btrfs.ko # cp -f fs/btrfs/btrfs.ko.zst /usr/lib/modules/$(uname -r)/kernel/fs/btrfs/
或者,您也可以将更新的模块放在 updates 文件夹中(如果不存在则创建它)。
$ cp fs/btrfs/btrfs.ko.zst /usr/lib/modules/$(uname -r)/updates
但是,如果您要添加一个新模块,您可以直接将其复制到 extramodules(请注意,这只是一个示例,btrfs 不会从此加载)。
# cp fs/btrfs/btrfs.ko.zst /usr/lib/modules/$(uname -r)/extramodules/
您需要使用 "depmod" 重新构建模块依赖树才能使用已安装的模块。
如果您正在为早期启动编译模块(例如更新的模块),并且将其复制到 Initramfs,那么您必须记住使用以下命令重新生成它(否则您编译的模块将不会被加载)。
# mkinitcpio -p linux
可能遇到的错误
如果 EXTRAVERSION 设置不正确,可能会出现以下错误:
# insmod mymod.ko insmod: ERROR: could not insert module mymod.ko: Invalid module format # modprobe mymod modprobe: ERROR: could not insert 'mymod': Exec format error
添加 force-vermagic 会忽略版本不匹配。
modprobe mymod --force-vermagic