内核/传统编译

来自 Arch Linux 中文维基

本文介绍从 kernel.org 的源代码编译自己的内核。这是一种对所有发行版均适用的传统方法。取决于你的背景,这也可能比用 ABS 工具编译内核更复杂。请考虑使用 Arch 构建系统以使重复的编译任务更加有效和安全。

准备[编辑 | 编辑源代码]

准备编译时,不需要也不建议使用 root 账号或用 sudo 等工具获取 root 权限。

安装核心软件包[编辑 | 编辑源代码]

安装 base-devel包组 软件组,这个组包含了 makegcc 等我们需要的软件包。参考 Arch 默认内核的 PKGBUILD,同时建议安装以下软件包:xmltokmodinetutils, bclibelfgitcpioperltarxz

创建内核编译目录[编辑 | 编辑源代码]

建议创建单独的内核编译目录,本文使用 home 下的 kernelbuild 目录。

$ mkdir ~/kernelbuild

获取源码[编辑 | 编辑源代码]

警告: systemd 需要至少 3.12 版本的内核(cgroups 支持需要 4.2 及以上的版本)。参考 /usr/share/doc/systemd/README

https://www.kernel.org 下载源码,得到你所选的内核版本对应的 tarball (tar.xz) 文件。

可以在浏览器中简单地右键 tar.xz 链接并选择 保存链接为 下载;也可以通过 HTTP、TFTPRsync 或者 Git 的各种图形化或命令行工具下载。

注意:
  • 验证下载到的任何内核 tarball 文件的 PGP 签名总是好的做法。这样可以确保它是合法的,并有助于建立信任网络。参见 kernel.org/signature
  • 对于某些版本(例如 -rc 版本),下载的存档和生成的目录不会严格遵循本页示例中使用的 A.B.C 命名,请根据你的需要进行调整。

下面例子中使用 wget 下载 A.B.C 内核到 ~/kernelbuild 目录:

$ cd ~/kernelbuild
$ wget https://cdn.kernel.org/pub/linux/kernel/vA.x/linux-A.B.C.tar.xz

在信任下载的压缩包之前,你还得验证下载的正确性。 首先抓取签名,然后用它来抓取签名密钥的指纹,然后使用指纹来获取实际的签名密钥:

$ wget https://cdn.kernel.org/pub/linux/kernel/vA.x/linux-A.B.C.tar.sign
$ gpg --list-packets linux-A.B.C.tar.sign
$ gpg --recv-keys fingerprint-from-previous-step

注意签名是为 tar 存档(即扩展名 .tar)生成的,而不是为下载的压缩 .tar.xz 文件生成的。 你需要解压后者而不解压它。 检查你是否已安装 xz,然后这样继续:

$ unxz linux-A.B.C.tar.xz
$ gpg --verify linux-A.B.C.tar.sign linux-A.B.C.tar

如果没看到"Good signature"就别继续。

如果在构建目录中没使用wget,就需要手动将压缩包移入其中,例如:

$ mv /path/to/linux-A.B.C.tar.xz ~/kernelbuild/

半官方内核镜像[编辑 | 编辑源代码]

一些 kernel.org Git 存储库的半官方镜像由其各自的维护者提供。这些通常比 kernel.org 的版本更新。

  • Linus Torvalds 放在 GitHub 上的主线内核

https://github.com/torvalds/linux.git.

  • Greg Kroah-Hartman 放在 GitHub 上的稳定分支

https://github.com/gregkh/linux. [1]

解压内核代码[编辑 | 编辑源代码]

  • 将其解压后进入源码目录:
$ tar -xvf linux-A.B.C.tar

为了绝对确保不发生权限错误,需要运行 chown 将文件夹的所有权转移给当前用户。

要将包含其中每个文件的文件夹的所有权转移,运行 chown 命令。

$ chown -R $USER:$USER linux-A.B.C 

文件夹中每个文件的所有权转移给你了,所以不会遇到任何与权限相关的错误。

为了完成准备工作,内核树需要绝对干净;不要觉得解压后源代码树是干净的。所以,切换到创建的新内核源目录,然后运行 make mrproper 命令:

$ cd linux-A.B.C
$ make mrproper
注意: make mrproper 的目标依赖于 make clean 的目标,所以没必要同时执行两者。参考[2]

配置[编辑 | 编辑源代码]

这是自定义内核以适应计算机的精确规格的最关键步骤。内核配置在 .config 文件中设置,其中包括 Kernel modules 的使用情况。正确设置 .config 中的选项,你的计算机会获得最高的运行效率。

注意: 现阶段无需使用 root 帐户或 root 权限。

参考使用下面内容可以节省时间:

  • 使用 Arch 官方内核的设置(推荐)
  • 生成正在运行内核的配置文件和设置(手动,适用于高级用户但不推荐)

导出当前内核设置[编辑 | 编辑源代码]

这个命令将使用默认的 Arch 内核设置为自定义内核创建一个 .config 文件。如果正在运行官方 Arch 内核,你可以在内核源码目录中使用以下命令:

$ zcat /proc/config.gz > .config

也可以在 official Arch Linux kernel 中找到默认配置。

提示:
  • 如果你在升级内核,一些选项可能改了或被删掉了。在这种情况下,在 #Compilation 下运行 make 时,它会要求你提供版本之间更改的每个配置选项的答案。要使用默认值而不用一直按Enter,请运行 make olddefconfig
  • modprobed-db 可用于从默认 Arch .config 中删除不需要的模块。获得 properly populated database 后,运行 make LSMOD=$HOME/.config/modprobed.db localmodconfigmodprobed.db 中删除所有不存在的模块。
警告: 如果你用当前的 .config 文件编译内核,别忘了在新的 .config 中的重命名内核版本,就是"CONFIG_LOCALVERSION"这项。或者用#高级配置中列出的用户界面之一中的"General Setup > Local version - append to kernel release"更改。如果跳过这一步,就有把当前内核覆盖的风险。

高级配置[编辑 | 编辑源代码]

提示:如果不想在启动和关闭时看到很多内核输出,可以禁用相关的调试选项。

有多种方式可以协助配置内核:

注意: 下面列出的工具提供每个内核功能的三个配置选项:y 表示启用,n 表示禁用,m 表示启用作为内核模块(必要时加载)。
  • make menuconfig: 老的 ncurses 界面,被 nconfig 取代
  • make nconfig: 新的命令行 ncurses 界面
  • make xconfig: 用户友好的图形界面,需要安装 packagekit-qt5。建议没有经验的用户使用此方法。
  • make gconfig: 和 xconfig 类似的图形界面,使用 gtk.

在内核源代码目录执行选择的方法,将生成一个全新的.config,可选参数默认是选中的,但是如果有老内核的 .config 文件,这些参数不一定会被选中。

改变配置文件并保存。建议在源代码目录外备份.config,因为你可能需要编译多次,直到所有的选项都配置正确。如果不确定每个选项的影响,每次编译只改变少量选项。如果无法启动新构建的内核,参见这里查看必须的配置项列表。从liveCD运行$ lspci -k #以列出使用中的内核模块的名字。最重要的是,你必须保留 systemd 需要的 CGROUPS 支持。

从live media运行 lspci -k # 会列出正在使用内核模块的名称。重中之重,你必须保留cgroup支持,这是systemd的依赖。参阅 Gentoo:Kernel/Gentoo Kernel Configuration GuideGentoo:Intel#KernelGentoo:Ryzen#Kernel(适用于 Intel 或 AMD Ryzen CPU)。

编译[编辑 | 编辑源代码]

如果希望 gcc 根据处理器指令集进行优化, 编辑内核目录下的 arch/x86/Makefile (i686) 或 arch/x86_64/Makefile (86_64)文件:

  • 查找 Processor type and features > Processor Family 中设置的处理器系列 CONFIG_MK8,CONFIG_MPSC,CONFIG_MCORE2,CONFIG_MATOM,CONFIG_GENERIC_CPU
  • 将 cc 调用选项设置为 -march=native,例如 cflags-$(CONFIG_MK8) += $(call cc-option,-march=native).

对 32bit 内核,需要编辑 arch/x86/Makefile_32.cpu 并设置处理器的 -march=native

编译时间将从15分钟到超过一小时不等。这很大程度依赖于选择了多少选项/模块和处理器性能。详情参考 Makeflags.config 配置好之后,在内核目录运行:

$ make
提示:为了更快地编译,可以使用 -jX 参数运行 make 以并行编译,其中 X 是整数。最快的编译通常是通过使用机器中 CPU 核心的数量来实现的; 例如,对于双核处理器,运行 make -j2。 参阅 Makepkg#Improving compile times

安装[编辑 | 编辑源代码]

安装模块[编辑 | 编辑源代码]

内核编译了,它的模块也随之而来。首先构建模块:

$ make modules
警告: 从这里开始,需要 root 权限执行命令,否则会失败.

编译完内核后编译模块:

# make modules_install

这会将编译后的模块复制到 /lib/modules/A.B.C/ 中。 这使得单独使用的各个内核的模块保持分离。

提示:如果系统需要正常 Linux 内核外的模块,需要在定制内核安装完成之后重新编译它们。Nvidia 显卡模块可以参考 NVIDIA#Custom kernel

拷贝内核到 /boot 目录[编辑 | 编辑源代码]

注意: 确保已从你的系统架构的目录复制 bzImage 内核文件。见下文。

内核编译过程将生成该内核的压缩 bzImage,如果没有,运行:

make bzImage

必须将这个 bzImage 复制到 /boot 目录并重命名。只要名称带有 vmlinuz- 前缀,你就可以根据需要命名内核。 在下面的示例中,已安装并编译的 A.B.C 内核已被复制并重命名为 vmlinuz-linuxAB

# cp -v arch/x86/boot/bzImage /boot/vmlinuz-linuxAB

制作初始化内存盘[编辑 | 编辑源代码]

初始化内存盘,是一个在真正的根文件系统可访问之前被挂载的初始化根目录文件系统。初始化内存盘(initrd)被绑定到对应的内核,且作为内核启动过程的一部分被载入。内核将该初始化内存盘(initrd)作为两阶段启动过程的一部分挂载起来,以载入模块使得真正的文件系统可用,然后得到真正的根文件系统。初始化内存盘包含实现以上过程的目录和可执行文件的最小集合,例如用来将内核模块安装到内核的insmod工具。对桌面或服务器Linux系统来说,初始化内存盘(initrd)就是一个临时的文件系统。其生命周期短暂,仅作为到真正的根文件系统的桥梁。在没有可变存储的嵌入式系统中,初始化内存盘就是永久的根文件系统。参考 Initramfs on Wikipediamkinitcpio

/boot 中的文件可以取任何名字。然而,为了方便区分,建议采取统一的命名规范,例如使用 linux<major revision><minor revision>

如果你使用LILO且其无法与内核设备映射器驱动连通,你可能需要先运行modprobe dm-mod命令。

自动生成[编辑 | 编辑源代码]

可以复制和修改现有的 mkinitcpio preset,以便可以按照与官方内核相同的方式生成自定义内核 initramfs image。 这在重新编译内核(例如更新时)时会节省很多时间。 在下面的示例中,将为上面安装的内核A.B.C复制并修改库存 Arch 内核的预设文件。


首先,复制现有的预设文件,将其重命名为与复制 bzImage 时指定为 /boot/vmlinuz- 后缀的自定义内核的名称相匹配:

# cp /etc/mkinitcpio.d/linux.preset /etc/mkinitcpio.d/linuxAB.preset

其次,编辑文件并针对自定义内核进行修改。注意 ALL_kver= 参数也与复制 bzImage 时指定的自定义内核的名称匹配:

/etc/mkinitcpio.d/linuxAB.preset
...
ALL_kver="/boot/vmlinuz-linuxAB"
...
default_image="/boot/initramfs-linuxAB.img"
...
fallback_image="/boot/initramfs-linuxAB-fallback.img"

最后,生成 initramfs 映像:

# mkinitcpio -p linuxAB

手动方法[编辑 | 编辑源代码]

不使用 preset 文件,可以用 mkinitcpio 手动生成:

# mkinitcpio -k <kernelversion> -g /boot/initramfs-<file name>.img
  • -k (--kernel <kernelversion>): 生成 initramfs image 的内核版本. <kernelversion> 需要和内核源代码目录和/usr/lib/modules/下的模块目录一致.
  • -g (--generate <filename>): 要生成的 initramfs 文件。

例如,上面安装的A.B.C自定义内核的命令为:

# mkinitcpio -k A.B.C -g /boot/initramfs-linuxAB.img

拷贝 System.map[编辑 | 编辑源代码]

System.map 文件并非 Linux 启动所必须的。它包含一张该次构建的内核的标识符之列表(例如函数名,变量名等等)和它们相应的地址。该“标识符到地址的映射”是用于:

  • 某些进程,像 klogd, ksymoops 等
  • OOPS 处理程序,当内核崩溃出错信息释放到屏幕时(例如类似于内核在某个函数中崩溃了的信息)。
提示:UEFI 分区使用 FAT32 文件系统,不支持软链接。

如果 /boot 支持软链接(即不是 FAT32),将 System.map 复制到 /boot, 然后创建 /boot/System.map 软链接到 /boot/System.map-YourKernelName:

# cp System.map /boot/System.map-YourKernelName
# ln -sf /boot/System.map-YourKernelName /boot/System.map

如果 /boot 是不支持软链接的 FAT32,则需要将第二条命令换成 cp 来复制文件。

完成以上所有步骤之后,你的/boot目录中应该多出以下三个文件和一个软链接:

  • Kernel: vmlinuz-YourKernelName
  • Initramfs: Initramfs-YourKernelName.img
  • System Map: System.map-YourKernelName
  • System.map 软链接(或者复件)

Bootloader 设置[编辑 | 编辑源代码]

在你的启动器(bootloader)配置文件中为你神奇的新内核添加一个入口。范例见GRUB, LILO, GRUB2Syslinux

提示:内核源码中有一个为LILO准备的脚本,以自动化实现该步骤:$ arch/x86/boot/install.sh。记得以root权限执行lilo来更新它。