Skip to the content.

10.Making the lfs system bootable


10.1 Introduction

现在应该配置 LFS 系统,使其可以引导了。本章讨论创建 /etc/fstab 文件,为新的 LFS 系统构建内核,以及安装 GRUB 引导加载器,使得系统引导时可以选择进入 LFS 系统。

10.2 Creating the /etc/fstab File

一些程序使用 /etc/fstab 文件,以确定哪些文件系统是默认挂载的,和它们应该按什么顺序挂载,以及哪些文件系统在挂载前必须被检查 (确定是否有完整性错误)。参考以下命令,创建一个新的文件系统表:

cat > /etc/fstab << "EOF"
# Begin /etc/fstab

# 文件系统     挂载点       类型     选项                转储  检查
#                                                            顺序

/dev/<xxx>     /            <fff>    defaults            1     1
/dev/<yyy>     swap         swap     pri=1               0     0
proc           /proc        proc     nosuid,noexec,nodev 0     0
sysfs          /sys         sysfs    nosuid,noexec,nodev 0     0
devpts         /dev/pts     devpts   gid=5,mode=620      0     0
tmpfs          /run         tmpfs    defaults            0     0
devtmpfs       /dev         devtmpfs mode=0755,nosuid    0     0
tmpfs          /dev/shm     tmpfs    nosuid,nodev        0     0

# End /etc/fstab
EOF

将 ** 和 ** 替换为适用于您的系统的值,例如 *sda2、sda5* 和 *ext4*。参阅 man 5 fstab 了解该文件中 6 个域的详细信息。

在挂载来源于 MS-DOS 或 Windows 的文件系统 (如 vfat、ntfs、smbfs、cifs、iso9660、udf) 时,需要一个特殊的挂载选项 —— utf8,才能正常解析文件名中的非 ASCII 字符。对于非 UTF-8 locale,选项 iocharset 的值应该和您的 locale 字符集设定一致,但改写成内核可以识别的写法。该选项能够正常工作的前提是,将相关的字符集定义 (在内核配置选项的 File Systems -> Native Language Support 子菜单中) 编译到内核中,或构建为内核模块。然而,如果使用了 UTF-8 locale,对应的 iocharset=utf8 会导致文件系统变得大小写敏感。为了避免这个问题,在使用 UTF-8 locale 时,需要用特殊选项 utf8 代替 iocharset=utf8。另外,vfat 和 smbfs 文件系统还需要“codepage”选项,它应该被设定为您的语言在 MS-DOS 下的代码页编号。例如,为了挂载一个 USB 闪存盘,一个 ru_RU.KOI8-R 用户应该在 /etc/fstab 中对应于闪存盘的行添加下列挂载选项:

noauto,user,quiet,showexec,codepage=866,iocharset=koi8r

相应的,ru_RU.UTF-8 用户应该使用下列选项:

noauto,user,quiet,showexec,codepage=866,utf8

注意此时使用的 iocharset 默认为 iso8859-1 (这保证文件系统是大小写不敏感的),而 utf8 选项告诉内核使用 UTF-8 编码转换文件名,这样它们就能在 UTF-8 locale 中被正确解析。

也可以在内核配置中,为一些文件系统指定默认 codepage 和 iocharset 选项值。相关的配置参数名为“Default NLS Option” (CONFIG_NLS_DEFAULT),“Default Remote NLS Option” (CONFIG_SMB_NLS_DEFAULT),“Default codepage for FAT” (CONFIG_FAT_DEFAULT_CODEPAGE),以及 “Default iocharset for FAT” (CONFIG_FAT_DEFAULT_IOCHARSET)。无法在编译内核时为 ntfs 文件系统指定这些默认值。

在某些硬盘上,通过将 barrier=1 挂载选项加入 /etc/fstab,可以使得 ext3 文件系统在发生电源故障时更可靠。为了检查磁盘驱动器是否支持该选项,在可用的磁盘驱动器上运行 hdparm。例如:

hdparm -I /dev/sda | grep NCQ

如果输出内容不为空,说明该选项可用。

注意:基于逻辑卷管理 (LVM) 的分区不能使用 barrier 选项。

10.3 Linux-6.1.11

Linux 软件包包含 Linux 内核。

估计构建时间: 1.5 - 130.0 SBU (一般约 12 SBU)

需要硬盘空间: 1200 - 8800 MB (一般约 1700 MB)

10.3.1 安装内核

构建内核需要三步 —— 配置、编译、安装。阅读内核源代码树中的 README 文件,了解不同于本手册的内核配置方法。

运行以下命令,准备编译内核:

make mrproper

该命令确保内核源代码树绝对干净,内核开发组建议在每次编译内核前运行该命令。尽管内核源代码树在解压后应该是干净的,但这并不完全可靠。

有多种配置内核选项的方法。例如,通常我们通过目录驱动的界面完成这一工作:

make menuconfig

以上命令中可选的 make 环境变量及含义:

*LANG= LC_ALL=*

它们根据宿主使用的 locale 建立 locale 设定。在 UTF-8 Linux 文本终端下,有时必须这样做才能正确绘制基于 ncurses 的配置菜单接口。

在这种情况下,一定要将 ** 替换成宿主环境中的 *$LANG* 变量值。您也可以使用宿主环境中 *$LC_ALL* 或 *$LC_CTYPE* 的值代替。

make menuconfig

这会启动 ncurses 目录驱动的界面。如果希望了解其他 (图形) 界面,可以输入 make help。

阅读 https://www.linuxfromscratch.org/hints/downloads/files/kernel-configuration.txt 了解关于内核配置的一般信息。BLFS 包含一些关于 LFS 之外的软件包需要的特定内核配置的信息,位于https://www.linuxfromscratch.org/blfs/view/11.3/longindex.html#kernel-config-index。另外在 http://www.kroah.com/lkn/ 也有一些关于配置和构建内核的信息。

注意

一个较好的初始内核配置可以通过运行 make defconfig 获得。它会考虑您的当前系统体系结构,将基本内核配置设定到较好的状态。

一定要按照以下列表启用/禁用/设定其中列出的内核特性,否则系统可能不能正常工作,甚至根本无法引导:

Processor type and features --->
    [*] Build a relocatable kernel [CONFIG_RELOCATABLE]
    [*]   Randomize the address of the kernel image (KASLR) [CONFIG_RANDOMIZE_BASE]
General setup --->
    [ ] Compile the kernel with warnings as errors [CONFIG_WERROR]
    < > Enable kernel headers through /sys/kernel/kheaders.tar.xz [CONFIG_IKHEADERS]
General architecture-dependent options  --->
    [*] Stack Protector buffer overflow detection [CONFIG_STACKPROTECTOR]
    [*]   Strong Stack Protector [CONFIG_STACKPROTECTOR_STRONG]
Device Drivers  --->
    Graphics support --->
        Frame buffer Devices --->
            <*> Support for frame buffer devices --->
        Console display driver support --->
            [*] Framebuffer Console support [CONFIG_FRAMEBUFFER_CONSOLE]
    Generic Driver Options  --->
        [ ] Support for uevent helper [CONFIG_UEVENT_HELPER]
        [*] Maintain a devtmpfs filesystem to mount at /dev [CONFIG_DEVTMPFS]
        [*]   Automount devtmpfs at /dev, after the kernel mounted the rootfs [CONFIG_DEVTMPFS_MOUNT]

如果在构建 64 位系统,还需要启用一些特性。如果使用 menuconfig 进行配置,需要首先启用 CONFIG_PCI_MSI,然后启用 CONFIG_IRQ_REMAP,最后启用 CONFIG_X86_X2APIC,这是因为只有选定了一个选项的所有依赖项后,该选项才会出现。

Processor type and features --->
    [*] Support x2apic [CONFIG_X86_X2APIC]
Device Drivers --->
    [*] PCI Support ---> [CONFIG_PCI]
        [*] Message Signaled Interrupts (MSI and MSI-X) [CONFIG_PCI_MSI]
    [*] IOMMU Hardware Support ---> [CONFIG_IOMMU_SUPPORT]
        [*] Support for Interrupt Remapping [CONFIG_IRQ_REMAP]

根据系统的需求,可能需要一些其他配置选项。BLFS 软件包需要的内核配置选项列表可以在 BLFS Index of Kernel Settings 查阅。

注意

如果您的硬件支持 UEFI,且您希望通过 UEFI 引导 LFS 系统,则您需要按照 BLFS 页面 的说明,调整一些内核配置选项。

上述配置选项的含义:

Randomize the address of the kernel image (KASLR)

为内核映像启用 ASLR,以预防一些基于内核中关键数据或代码的固定地址的攻击。

Compile the kernel with warnings as errors

如果使用了和内核开发者不同的编译器和/或配置,启用该选项可能导致构建失败。

Enable kernel headers through /sys/kernel/kheaders.tar.xz

启用该选项将会导致构建内核需要 cpio。LFS 没有安装 cpio。

Strong Stack Protector

为内核启用 SSP。我们通过在配置 GCC 时使用 –enable-default-ssp,已经为所有用户态代码启用了它,但是内核并不使用 GCC 默认的 SSP 设定。因此我们在这里显式地启用它。

Support for uevent helper

如果启用了该选项,它可能干扰 Udev/Eudev 的设备管理。

Maintain a devtmpfs

该选项会使内核自动创建设备节点,即使 Udev 没有运行。Udev 之后才在这些设备节点的基础上运行,管理它们的访问权限并为它们建立符号链接。所有 Udev/Eudev 用户都需要启用该选项。

Automount devtmpfs at /dev

该选项使得内核在切换到根文件系统之后,执行 init 前,将内核获知的设备信息挂载到 /dev。

Framebuffer Console support

该选项用于在帧缓冲设备显示 Linux 控制台。为了使得内核能够在早期引导阶段打印调试信息,不应该将其构建为内核模块,除非将要使用 initramfs。另外,如果 CONFIG_DRM (Direct Rendering Manager) 被启用,通常来说也应该启用 CONFIG_DRM_FBDEV_EMULATION。

Support x2apic

支持以 x2APIC 模式运行 64 位 x86 处理器的中断控制器。64 位 x86 系统的固件可能启用了 x2APIC,此时未启用该选项的内核在引导时会发生内核恐慌。该选项在固件禁用 x2APIC 时没有作用,但无害。

某些情况下,make oldconfig 更为合适。阅读 README 文件了解更多信息。

如果希望的话,也可以将宿主系统的内核配置文件 .config 拷贝到解压出的 linux-6.1.11 目录 (前提是可以找到该文件)。然而我们不推荐这样做,一般来说,浏览整个配置目录,并从头创建内核配置是更好的选择。

编译内核映像和模块:

make

如果要使用内核模块,可能需要在 /etc/modprobe.d 中写入模块配置。讨论模块和内核配置的信息位于第 9.3 节 “设备和模块管理概述”linux-6.1.11/Documentation 目录下的内核文档中。另外 modprobe.d(5) 也可以作为参考。

如果内核配置使用了模块,安装它们:

make modules_install

在内核编译完成后,需要进行额外步骤完成安装,一些文件需要拷贝到 /boot 目录中。

小心

如果要使用单独的 /boot 分区 (包括和宿主发行版共用一个 /boot 分区的情况),需要将这些文件拷贝到该分区中。最简单的方法是首先在 /etc/fstab 中加入 /boot 分区的条目 (详见前一节),然后在 chroot 环境中以 root 身份执行以下命令:

mount /boot

该命令中省略了指向设备节点的路径,因为 mount 可以从 /etc/fstab 中读取它。

指向内核映像的路径可能随机器平台的不同而变化。下面使用的文件名可以依照您的需要改变,但文件名的开头应该保持为 vmlinuz,以保证和下一节描述的引导过程自动设定相兼容。下面的命令假定机器是 x86 体系结构:

cp -iv arch/x86/boot/bzImage /boot/vmlinuz-6.1.11-lfs-11.3

System.map 是内核符号文件,它将内核 API 的每个函数入口点和运行时数据结构映射到它们的地址。它被用于调查分析内核可能出现的问题。执行以下命令安装该文件:

cp -iv System.map /boot/System.map-6.1.11

内核配置文件 .config 由上述的 make menuconfig 步骤生成,包含编译好的内核的所有配置选项。最好能将它保留下来以供日后参考:

cp -iv .config /boot/config-6.1.11

安装 Linux 内核文档:

install -d /usr/share/doc/linux-6.1.11
cp -r Documentation/* /usr/share/doc/linux-6.1.11

需要注意的是,在内核源代码目录中可能有不属于 root 的文件。在以 root 身份解压源代码包时 (就像我们在 chroot 环境中所做的那样),这些文件会获得它们之前在软件包创建者的计算机上的用户和组 ID。这一般不会造成问题,因为在安装后通常会删除源代码目录树。然而,Linux 源代码目录树一般会被保留较长时间,这样创建者当时使用的用户 ID 就可能被分配给本机的某个用户,导致该用户拥有内核源代码的写权限。

注意

之后在 BLFS 中安装软件包时往往需要修改内核配置。因此,和其他软件包不同,我们在安装好内核后可以不移除源代码树。

如果要保留内核源代码树,对内核源代码目录运行 chown -R 0:0 命令,以保证 linux-6.1.11 目录中所有文件都属于 root

警告

有的内核文档建议创建符号链接 /usr/src/linux 指向内核源代码目录,这仅仅适用于 2.6 系列之前的内核。在 LFS 系统上绝对不要创建它,因为在构建完基本 LFS 系统后,它可能在您构建其他软件包时引起问题。

警告

在系统 include 目录 (即 /usr/include) 中的内核头文件应该总是与构建 Glibc 时使用的内核头文件一致,即保持为第 5.4 节 “Linux-6.1.11 API 头文件”中安装的净化头文件。换句话说,永远不要用原始内核头文件,或其他版本内核的净化头文件替换它们。

10.3.2 配置 Linux 内核模块加载顺序

多数情况下 Linux 内核模块可以自动加载,但有时需要指定加载顺序。负责加载内核模块的程序 modprobe 和 insmod 从 /etc/modprobe.d 下的配置文件中读取加载顺序,例如,如果 USB 驱动程序 (ehci_hcd、ohci_hcd 和 uhci_hcd) 被构建为模块,则必须按照先加载 echi_hcd,再加载 ohci_hcd 和 uhci_hcd 的正确顺序,才能避免引导时出现警告信息。

为此,执行以下命令创建文件 /etc/modprobe.d/usb.conf

install -v -m755 -d /etc/modprobe.d
cat > /etc/modprobe.d/usb.conf << "EOF"
# Begin /etc/modprobe.d/usb.conf

install ohci_hcd /sbin/modprobe ehci_hcd ; /sbin/modprobe -i ohci_hcd ; true
install uhci_hcd /sbin/modprobe ehci_hcd ; /sbin/modprobe -i uhci_hcd ; true

# End /etc/modprobe.d/usb.conf
EOF

10.3.3 Linux 的内容

安装的文件: config-6.1.11, vmlinuz-6.1.11-lfs-11.3, 以及 System.map-6.1.11

安装的目录: /lib/modules 和 /usr/share/doc/linux-6.1.11

简要描述

config-6.1.11 包含所有内核配置选项的值

vmlinuz-6.1.11-lfs-11.3 Linux 系统的引擎,在启动计算机时,它是操作系统中被最早加载的部分。它检测并初始化计算机硬件,将它们以目录树的形式提供给软件,并将单个 CPU 封装成多任务系统,使多个用户程序看上去在同时执行

System.map-6.1.11 地址和符号列表;它将内核函数和数据结构映射为入口点和地址

10.4 Using GRUB to Set Up the Boot Process

注意

如果您的系统支持 UEFI,且您希望通过 UEFI 引导 LFS,您应该跳过本页,并按照 BLFS 页面中的说明,配置支持 UEFI 的 GRUB。

10.4.1 概述

警告

如果您不小心错误地配置了 GRUB,可能导致您的系统完全无法使用,除非使用 CD-ROM 或可引导的 USB 存储器等备用引导设备。本节不是引导您的 LFS 系统的唯一方案,您可能只要修改现有的启动加载器 (如 Grub-Legacy、GRUB2 或 LILO) 配置即可引导 LFS。

您务必保证自己拥有一个紧急引导磁盘,它在计算机不可用 (无法引导) 时能够 “抢修” 计算机。如果您现在还没有引导设备,您可以执行以下命令创建一个。在运行下列命令前,您需要跳到 BLFS,安装包含 xorriso 的 libisoburn 软件包:

cd /tmp
grub-mkrescue --output=grub-img.iso
xorriso -as cdrecord -v dev=/dev/cdrw blank=as_needed grub-img.iso

10.4.2 GRUB 命名惯例

GRUB 使用一种独特的命名结构,为驱动器和分区命名。分区名的形式为 (hdn,m),这里 n 是硬盘驱动器编号,m 是分区编号。硬盘驱动器编号从 0 开始,但分区号对于主分区来说从 1 开始 (对于扩展分区来说从 5 开始)。例如,分区 sda1 在 GRUB 中的名字是 (hd0,1),而 sdb3 的名字是 (hd1,3)。和 Linux 不同,GRUB 不认为 CD-ROM 驱动器属于硬盘驱动器。例如,如果在 hdb 上有一个 CD-ROM 驱动器,而 hdc 上有第二个硬盘驱动器,则第二个硬盘驱动器仍然名为 hd1

10.4.3 设定 GRUB 配置

GRUB 的工作方式是,将数据写入硬盘的第一个物理磁道。这里不属于任何文件系统,在启动时,第一个物理磁道中的程序从引导分区加载 GRUB 模块,默认在 /boot/grub 中查找模块。

引导分区的位置由负责进行配置的用户自己决定,作者推荐创建一个小的 (建议大小为 200 MB) 分区,专门存放引导信息。这样,不同的 Linux 系统 (无论是 LFS 还是商业发行版) 在启动时和启动后都能访问相同的引导文件。如果您选择这样做,您需要挂载这个单独的分区,将 /boot 中已有的文件 (例如上一节中构建的 Linux 内核) 移动到新的分区中。之后,解除该分区的挂载,并将它挂载为 /boot。另外,还要注意更新 /etc/fstab

直接将 /boot 目录保留在 LFS 分区也是可以的,但这样在配置多系统启动时比较麻烦。

根据以上信息,确定 LFS 根分区 (或 boot 分区,如果使用了独立的 boot 分区) 的名称。下面假设 LFS 根分区 (或 boot 分区) 是 sda2

将 GRUB 文件安装到 /boot/grub 并设定引导磁道:

警告

以下命令会覆盖当前启动引导器,如果这不是您希望的,不要运行该命令。例如,如果您使用第三方启动引导器管理主引导记录 (MBR)。

grub-install /dev/sda

注意

如果系统是使用 UEFI 引导的,grub-install 会试图为 x86_64-efi 目标安装文件,但它们并未在第 8 章中安装。如果出现了这类问题,请在以上命令中添加 --target i386-pc 选项。

10.4.4 创建 GRUB 配置文件

生成 /boot/grub/grub.cfg

cat > /boot/grub/grub.cfg << "EOF"
# Begin /boot/grub/grub.cfg
set default=0
set timeout=5

insmod ext2
set root=(hd0,2)

menuentry "GNU/Linux, Linux 6.1.11-lfs-11.3" {
        linux   /boot/vmlinuz-6.1.11-lfs-11.3 root=/dev/sda2 ro
}
EOF

注意

从 GRUB的视角来看,内核文件的位置相对于它使用的分区。如果您使用了单独的 /boot 分区,需要从上面的 linux 行删除 /boot,然后修改 set root 行,指向 /boot 分区。

注意

如果新增或移除了一些存储设备 (包括 USB 闪存盘等可移动存储设备),则 GRUB 赋予分区的编号可能发生改变。这可能导致引导失败,因为 grub.cfg 仍然在使用“旧的”编号。如果希望避免这种问题,可以使用分区和文件系统的 UUID 指定分区,以代替 GRUB 编号。运行 lsblk -o UUID,PARTUUID,PATH,MOUNTPOINT 以显示文件系统 (在 UUID 列) 和分区 (在 PARTUUID 列) 的 UUID。之后将 set root=(hdx,y) 替换为 search --set=root --fs-uuid *<内核所在文件系统的 UUID="">*,并将 `root=/dev/sda2` 替换为 `root=PARTUUID=<构建 LFS="" 使用的分区的="" UUID="">`。

注意分区的 UUID 和该分区中文件系统的 UUID 是完全不同的。一些在线资料可能建议使用 root=UUID=<文件系统 UUID> 代替root=PARTUUID=<分区 UUID>,但是这种方法依赖于 initramfs,而 initramfs 超出了 LFS 的范畴。

/dev 中分区对应的设备节点名也可能发生改变 (和 GRUB 分区编号的变化相比较为少见)。在 /etc/fstab 中,也可以将 /dev/sda1 这样的设备节点路径改为 PARTUUID=<分区 UUID>,从而避免设备节点命名发生改变时可能导致的引导失败。

GRUB 是一个很强大的程序,它提供了非常多的选项,可以支持多种设备、操作系统和分区类型,还有很多用于定制启动屏幕、声音、鼠标输入等的选项。这些选项的细节超过了本书的范围,不予讨论。

小心

有一个命令 grub-mkconfig 被用于自动创建配置文件。它使用 /etc/grub.d 中的脚本创建新配置文件,这会覆盖您手动编写的 grub.cfg。这些脚本主要是为非源代码发行版设计的,在 LFS 中不推荐使用。但是,如果您安装了商业发行版,它很可能在发行版中被运行,记得备份 grub.cfg 以防它被覆盖。