编译内核模块
有时您可能希望编译 Linux 的内核模块,而无需重新编译整个内核。
构建环境
首先,您需要安装构建依赖项,例如编译器 (base-devel) 和 linux-headers。
接下来,您需要获取模块计划运行的内核版本的源代码。您可以尝试使用较新的内核源代码,但编译后的模块很可能无法加载。
如果目标内核版本是已安装的内核,请使用以下命令查找其版本
$ 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
,并且目标内核版本是正在运行的内核,您可以使用其配置文件
$ 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
或者,如果您很乐意使用 modprobe 加载模块,并使用 --force-vermagic
选项来忽略内核版本号中的不匹配,您可以简单地运行
$ make modules_prepare
+
字符附加到 LOCALVERSION 配置设置。例如: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