使 LFS 系统可引导

创建 /etc/fstab 文件

  • 执行以下命令创建 /etc/fstab

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

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

#/dev/<xxx>     /            <fff>      defaults            1     1
#/dev/<yyy>     swap         swap       pri=1               0     0

/dev/disk/by-uuid/81368941-bd96-4539-bf8e-7215d532a872 / xfs defaults 0 1
/dev/disk/by-uuid/DA73-A40D /boot/efi vfat defaults 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
cgroup2        /sys/fs/cgroup cgroup2  nosuid,noexec,nodev 0     0

# End /etc/fstab
EOF

备注

这里配置 <xxx>、<yyy> <fff> 参考之前 LFS分区( mbp15_late_2013)

Linux内核

备注

在LFS网站有两篇比较古老但是依然有参考价值的文档:

配置内核
cd /sources

kernel_VERSION=6.10.5
tar xf linux-${kernel_VERSION}.tar.xz
cd linux-${kernel_VERSION}

# 准备编译内核,先确保内核源代码完全干净
make mrproper

# 执行获得一个初始化内核配置 .config
# Default configuration is based on 'x86_64_defconfig'
make defconfig

# 在初始配置基础上进行定制
make menuconfig

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

配置部分必要特性
General setup --->
  [ ] Compile the kernel with warnings as errors                        [WERROR]
  CPU/Task time and stats accounting --->
    [*] Pressure stall information tracking                                [PSI]
    [ ]   Require boot parameter to enable pressure stall information tracking
                                                     ...  [PSI_DEFAULT_DISABLED]
  < > Enable kernel headers through /sys/kernel/kheaders.tar.xz      [IKHEADERS]
  [*] Control Group support --->                                       [CGROUPS]
    [*] Memory controller                                                [MEMCG]
  [ ] Configure standard kernel features (expert users) --->            [EXPERT]

Processor type and features --->
  [*] Build a relocatable kernel                                   [RELOCATABLE]
  [*]   Randomize the address of the kernel image (KASLR)       [RANDOMIZE_BASE]

General architecture-dependent options --->
  [*] Stack Protector buffer overflow detection                 [STACKPROTECTOR]
  [*]   Strong Stack Protector                           [STACKPROTECTOR_STRONG]

Device Drivers --->
  Generic Driver Options --->
    [ ] Support for uevent helper                                [UEVENT_HELPER]
    [*] Maintain a devtmpfs filesystem to mount at /dev               [DEVTMPFS]
    [*]   Automount devtmpfs at /dev, after the kernel mounted the rootfs
                                                           ...  [DEVTMPFS_MOUNT]
  Graphics support --->
    < /*/M> Direct Rendering Manager (XFree86 4.1.0 and higher DRI support) --->
                                                                      ...  [DRM]
      # If [DRM] is selected as * or M, this must be selected:
      [ /*] Enable legacy fbdev support for your modesetting driver
                                                      ...  [DRM_FBDEV_EMULATION]
    Console display driver support --->
      # If [DRM] is selected as * or M, this must be selected:
      [ /*] Framebuffer Console support                    [FRAMEBUFFER_CONSOLE]

在构建 64 位系统,还需要启用一些特性:

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

Processor type and features --->
  [*] Support x2apic                                                [X86_X2APIC]

Device Drivers --->
  [*] PCI support --->                                                     [PCI]
    [*] Message Signaled Interrupts (MSI and MSI-X)                    [PCI_MSI]
  [*] IOMMU Hardware Support --->                                [IOMMU_SUPPORT]
    [*] Support for Interrupt Remapping                              [IRQ_REMAP]
配置内核-NVMe
Device Drivers --->
  NVME Support --->
    <*> NVM Express block device

<*> NVM Express block device
[*] NVMe multipath support
[*] NVMe hardware monitoring
< > NVM Express over Fabrics FC host driver
< > NVM Express over Fabrics TCP host driver
[ ] NVMe over Fabrics In-Band Authentication in host side

以下配置我没有找到
<M> NVMe Target support
  [*]   NVMe Target Passthrough support
  <M>   NVMe loopback device support
  <M>   NVMe over Fabrics FC target driver
  < >     NVMe over Fabrics FC Transport Loopback Test driver (NEW)
  <M>   NVMe over Fabrics TCP target support
  • 由于我使用XFS:

配置内核XFS
File system --->
  <*> XFS filesystem support
  [ ]   Support deprecated V4 (crc=0) format
  [ ]   Support deprecated case-insensitive ascii (ascii-ci=1) format
  [*]   XFS Quota Support
  [*]   XFS POSIX ACL Support
  [*]   XFS Realtime subvolume Support
  [*] XFS online metadata check Support
  [*]   XFS online metadata check usage data collection (NEW)
  [*]   XFS online metadata repair Support
  [ ] XFS Verbose Warnings
  [ ] XFS Debugging support
  
  • 编译和安装:

编译内核
#编译内核映像和模块
make

#安装内核模块
make modules_install

# 复制编译好的内核
cp -iv arch/x86/boot/bzImage /boot/vmlinuz-6.10.5-lfs-12.2

# System.map 是内核符号文件,它将内核 API 的每个函数入口点和运行时数据结构映射到它们的地址。它被用于调查分析内核可能出现的问题。
cp -iv System.map /boot/System.map-6.10.5

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

# 安装 Linux 内核文档
cp -r Documentation -T /usr/share/doc/linux-6.10.5

配置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

创建 /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

设置GRUB

警告

由于我使用EFI引导,所以这段参考BLFS的 Using GRUB to Set Up the Boot Process with UEFI

内核UEFI支持配置

在内核配置中启用UEFI支持

内核UEFI支持配置
Processor type and features --->
  [*] EFI runtime service support                                          [EFI]
  [*]   EFI stub support                                              [EFI_STUB]

-*- Enable the block layer --->                                          [BLOCK]
  Partition Types --->
    [ /*] Advanced partition selection                      [PARTITION_ADVANCED]
    [*]     EFI GUID Partition support                           [EFI_PARTITION]

Device Drivers --->
  Firmware Drivers --->
    [*] Mark VGA/VBE/EFI FB as generic system framebuffer       [SYSFB_SIMPLEFB]
  Graphics support --->
    <*> Direct Rendering Manager (XFree86 4.1.0 and higher DRI support) --->
                                                                      ...  [DRM]
      [*] Enable legacy fbdev support for your modesetting driver
                                                      ...  [DRM_FBDEV_EMULATION]
      <*> Simple framebuffer driver                              [DRM_SIMPLEDRM]
    Console display driver support --->
      [*] Framebuffer Console support                      [FRAMEBUFFER_CONSOLE]

File systems --->
  DOS/FAT/EXFAT/NT Filesystems --->
    <*/M> VFAT (Windows-95) fs support                                 [VFAT_FS]
  Pseudo filesystems --->
    <*/M> EFI Variable filesystem                                    [EFIVAR_FS]
  -*- Native language support --->                                         [NLS]
    <*/M> Codepage 437 (United States, Canada)                [NLS_CODEPAGE_437]
    <*/M> NLS ISO 8859-1  (Latin 1; Western European Languages)  [NLS_ISO8859_1]

创建紧急救援启动磁盘

这段创建一个启动U盘的工作我没有做,如有需要参看原文

找到或创建EFI系统分区

EFI系统中,bootloader是安装在一个特殊的称为 EFI System Partition(ESP) FAT32分区。通常已经有Linux安装或者Windows安装的系统,可能已经创建好了ESP分区。

  • (这步我没有做,因为我已经有一个ESP分区挂载到 /boot )如果是首次构建系统,创建以下ESP分区(案例是 /dev/sda )

创建ESP分区
mount --mkdir -v -t vfat /dev/sda1 -o codepage=437,iocharset=iso8859-1 \
      /boot/efi
  • (我没有做)对应配置 /etc/fstab :

ESP分区挂载配置
cat >> /etc/fstab << EOF
/dev/sda1 /boot/efi vfat codepage=437,iocharset=iso8859-1 0 1
EOF

在我的实践中,我的磁盘分区是在 /dev/nvme0n1 上划分的两个分区,分区1是 ESP 分区,已经在 LFS分区( mbp15_late_2013) 中完成过了。

GRUB和EFI的最小化启动配置

在基于 UEFI 的系统上,GRUB 通过将 EFI 应用程序(一种特殊的可执行文件)安装到 ESP 中来工作。EFI 固件将从记录在 EFI 变量中的引导条目以及 硬编码路径 EFI/BOOT/BOOTX64.EFI 中搜索 EFI 应用程序中的引导加载程序。

要在硬编码路径 EFI/BOOT/BOOTX64.EFI 中安装带有 EFI 应用程序的 GRUB,首先确保启动分区安装在 /boot 上,ESP 安装在 /boot/efi 上。

备注

注意,如果ESP分区挂载在 /boot/efi 目录,那么 grub-install 不需要任何参数。但是,我的 ESP 分区改在在 /boot 分区,直接执行 grub-install 会提示找不到 EFI 目录。这个问题在 Gentoo GRUB 已经有实践经验。

我在 Gentoo GRUB 有实践记录,所以这里参考执行行如下(需要指定安装路径):

安装EFI
# 如果ESP分区挂载在 /boot 上,则是非标,需要指定efi目录
grub-install --target=x86_64-efi --removable --efi-directory=/boot

# 如果ESP分区挂载在 /boot/efi 上,则是标准路径
grub-install --target=x86_64-efi --removable

备注

这段话非常重要,请仔细体会

从GRUB视角来看,内核文件的位置相对于它使用的分区:

  • 如果使用独立的 /boot 分区(ESP分区挂载到 /boot 上),那么 grub 和boot分区在一个磁盘分区起点( /boot 为起点),从grub角度看内核和它是并列的,所以此时内核位置是 /vmlinuz-6.10.5-lfs-12.2 ,也就是要去掉 /boot -- 请注意 MacBook Pro上运行Arch Linux 就是这种方式)

  • 如果没有使用独立的 /boot 分区,也就是ESP分区是挂载到 /boot/efi 上,那么分区起点就是 / ,此时从grub看分区访问内核的路径就是 /boot/vmlinuz-6.10.5-lfs-12.2 。这种方式是发行版主要采用的方式,都是采用 ESP 分区挂载为 /boot/efi ,所以你在 /boot/grub/grub.cfg 中看到的配置是 /boot/vmlinuz-6.10.5-lfs-12.2

备注

为了确保主机磁盘增减时候GRUB访问磁盘分区引导依然正确,应该将 /dev/sda 这样的设备路径改为分区和文件系统的UUID:

  • set root=(hdx,y) 替换为 search --set=root --fs-uuid <内核所在文件系统的 UUID> (注意是 磁盘文件系统UUID

  • root=/dev/sda2 替换为 root=PARTUUID=<构建 LFS 使用的分区的 UUID> (注意是 分区UUID )

上述 search --set=root ... 方法在主流发行版 Ubuntu Linux 上就是这么设置的,可以参考

挂载EFI变量文件系统(efivars)

备注

我在 Ubuntu Linux 系统上看到默认就是挂载 efivars 文件系统,但是没有配置 /etc/fstab 就能挂载,可能是在其他地方有配置。我这里按照LFS的文档来完成

在UEFI平台安装GRUB需要EFI变量文件系统(EFI Variable file sysem, efivarfs)挂载。所以这里执行:

挂载 efivarifs
mountpoint /sys/firmware/efi/efivars || mount -v -t efivarfs efivarfs /sys/firmware/efi/efivars

这个信息似乎是因为我的Host主机 Ubuntu Linux 已经挂载了 efivarfs

/etc/fstab 中添加 efivarifs 配置
cat >> /etc/fstab << EOF
efivarfs /sys/firmware/efi/efivars efivarfs defaults 0 0
EOF

设置配置

我这里的ESP分区挂载在 /boot 不是标准的方法,后续实践应该挂载为 /boot/efi

建议系统安装 efibootmgr ,这样就能够执行 efibootmgr | cut -f 1 检查EFI中启动配置

当前系统 efibootmgr 内容
BootCurrent: 000F
Timeout: 0 seconds
BootOrder: 0002,0008,000F,0009,000A,000B,000C,000D,0010,000E,0011,0015,0000,0001,0003,0004,0005,0006,0007,0012
Boot0000  Embedded UEFI Shell
Boot0001  Diagnose Error
Boot0002  System Utilities
Boot0003  Intelligent Provisioning
Boot0004  Boot Menu
Boot0005  Network Boot
Boot0006  Embedded Diagnostics
Boot0007  View Integrated Management Log
Boot0008* Generic USB Boot
Boot0009* Embedded SATA Port 1 HDD : INTEL SSDSC2KW512G8 
Boot000A* Embedded SATA Port 2 HDD : ST9500420AS 
Boot000B* Embedded SATA Port 3 HDD : HGST HTS725050A7E630 
Boot000C* Embedded SATA Port 4 HDD : ST9500325AS 
Boot000D* Embedded FlexibleLOM 1 Port 1 : HPE Ethernet 1Gb 4-port 366FLR Adapter - NIC (PXE IPv4) 
Boot000E* Embedded FlexibleLOM 1 Port 1 : HPE Ethernet 1Gb 4-port 366FLR Adapter - NIC (PXE IPv6) 
Boot000F* ubuntu
Boot0010* Embedded FlexibleLOM 1 Port 2 : HPE Ethernet 1Gb 4-port 366FLR Adapter - NIC (PXE IPv4) 
Boot0011* Embedded FlexibleLOM 1 Port 2 : HPE Ethernet 1Gb 4-port 366FLR Adapter - NIC (PXE IPv6) 
Boot0012* Slot 2 : NVM Express Controller - PHBT85100270016N   -INTEL MEMPEK1J016GAL                   -D5E4D25C
Boot0013* Front USB 2 : SanDisk' Cruzer Fit
Boot0014* Slot 1 : NVM Express Controller - S676NF0R908144     -SAMSUNG MZVL21T0HCLR-00B00             -B9382500
Boot0015* Embedded SATA Port 5 HDD : SDLF1CRR-019T-1HA1

可以看到,我的系统启动优先级是 Boot0002  System Utilities => Boot0008* Generic USB Boot => Boot000F* ubuntu ...

也就是为何我现在系统能够进入Ubuntu,是因为它排在第三启动顺序

我现在的LFS系统安装在 Boot0012* Slot 2 : NVM Express Controller - PHBT85100270016N   -INTEL MEMPEK1J016GAL                   -D5E4D25C

这里启动的第一顺序是 Boot0002  System Utilities ,在HP服务器的iLO管理界面,通过调整系统启动磁盘顺序,就是调整这个 System Utilities 启动顺序(不过在 efibootmagr 中看不到)

不过,最好还是通过 efibootmgr 在命令好解决,类似 Gentoo Linux 安装实践

备注

我实际是通过HP服务器的iLO启动顺序来管理的

创建GRUB配置

/boot/grub/grub.cfg 配置文件中 root= 配置部分需要根据实际情况调整,我这里采用了UUID指定磁盘分区2,这个UUID也就是 /etc/fstab 中的配置

创建 /boot/grub/grub.cfg 配置文件
cat > /boot/grub/grub.cfg << EOF
# Begin /boot/grub/grub.cfg
default=0
timeout=15

insmod part_gpt
insmod ext2

insmod efi_gop
insmod efi_uga
if loadfont /boot/grub/fonts/unicode.pf2; then
  terminal_output gfxterm
fi


menuentry "GNU/Linux, Linux 6.10.5-lfs-12.2" {
  search --set=root --fs-uuid 81368941-bd96-4539-bf8e-7215d532a872
  linux   /boot/vmlinuz-6.10.5-lfs-12.2 root=PARTUUID=cf8d6f20-92bf-4f81-982e-50bea63e6ca3 ro
}

menuentry "Ubuntu, Linux 5.15.0-126-generic" {
  search --set=root --fs-uuid caa4193b-9222-49fe-a4b3-89f1cb417e6a
  linux   /boot/vmlinuz-5.15.0-126-generic root=UUID=caa4193b-9222-49fe-a4b3-89f1cb417e6a ro
  initrd  /boot/initrd.img-5.15.0-126-generic
}

menuentry "Firmware Setup" {
  fwsetup
}
EOF

这里我添加了2行配置GRUB串口输出,参考 How does one set up a serial terminal and/or console in Red Hat Enterprise Linux?

异常排查

启动以后Grub菜单页面显示之后,一旦选择并回车进入选项,则提示:

提示无法找到vmlinuz内核
error: file '/boot/vmlinuz-6.10.5-lfs-12.2' not found.

Press any key to continue...

但是没有进一步提示,我不确定是不是磁盘挂载问题

我尝试把 grub.cfg 配置项复制到同一台服务器的 ubuntu 磁盘的 grub.cfg 配置中,然后在 HPE ProLiant DL360 Gen9服务器HP服务器iLO技术 WEB管理界面中调整启动磁盘顺序,将 ubuntu 磁盘调整到前面。

这次 ubuntu 磁盘启动的gurb有详细信息,显示出错误内容:

切换到ubuntu磁盘启动后grub信息
VFS: Cannot open root device "UUID=81368941-bd96-4539-bf8e-7215d532a872" or unknown-block(0,0): error -6
Please append a correct "root=" boot option: here are the available partitions:
103:00000   14065664 nvme0n1
 (driver?)
  103:00001    373760 nvme0n1p1 85af5570-6841-45d2-b749-bd0fab135a8a
  103:00002  13689856 nvme0n1p2 cf8d6f20-92bf-4f81-982e-50bea63e6ca3

0800      1875374424 sda
 driver: sd
  0001      f99998720 sda1 ba7c6674-5d6e-4aa5-a2ed-cb0e265b73b7
  0002      125000704 sda2 8c19863f-e149-45d9-b217-d3ca9c46862f

0810      500107608 sdb
...

这个问题在LFS文档中其实有说明,需要仔细阅读理解:

  • 关键点是 set root 指定哪个磁盘文件系统来搜索内核,我最初照抄文档中 set root=(hd0,2) 存在的问题是我的服务器有多块硬盘,启动以后存在LFS的磁盘并不一定会固定是额卑微 hd0 ,所以应该修订为 search --set=root --fs-uuid <内核所在文件系统的 UUID>

  • 另一个关键点是 ESP 分区是挂载为 /boot 还是 /boot/efi ,这决定了内核获取从磁盘分区开始计算到底要不要包含 /boot (见上文)

  • 我已经修订上文的配置文件

最终完成

最终成功后的服务器登陆后检查

磁盘使用
-bash-5.2# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/root        14G  1.6G   12G  12% /
devtmpfs        189G     0  189G   0% /dev
tmpfs           189G  1.6M  189G   1% /run
tmpfs           189G     0  189G   0% /dev/shm
/dev/nvme0n1p1  365M  188K  365M   1% /boot/efi
/dev/sda2       117G   13G   98G  12% /sources
efivarfs        176K   87K   85K  51% /sys/firmware/efi/efivars

可以看到一个基础的Linux系统只需要 1.6G 磁盘就可以运行起来

内存使用
-bash-5.2# cat /proc/meminfo 
MemTotal:       396110968 kB
MemFree:        395551992 kB
MemAvailable:   393931812 kB
Buffers:            6364 kB
Cached:            44116 kB
SwapCached:            0 kB
Active:            30416 kB
Inactive:          22696 kB
Active(anon):        104 kB
Inactive(anon):     7216 kB
Active(file):      30312 kB
Inactive(file):    15480 kB
Unevictable:        3072 kB
Mlocked:               0 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:                 0 kB
Writeback:             0 kB
AnonPages:          5868 kB
Mapped:            11560 kB
Shmem:              4680 kB
KReclaimable:      19964 kB
Slab:              86756 kB
SReclaimable:      19964 kB
SUnreclaim:        66792 kB
KernelStack:        7648 kB
PageTables:          972 kB
SecPageTables:      2056 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:    198055484 kB
Committed_AS:      14936 kB
VmallocTotal:   34359738367 kB
VmallocUsed:      228548 kB
VmallocChunk:          0 kB
Percpu:            11904 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
Hugetlb:               0 kB
DirectMap4k:       31228 kB
DirectMap2M:     5079040 kB
DirectMap1G:    399507456 kB

Linux内存管理 需要分析