Skip to content

自己跑了一遍 Linux 启动流程,这回算是理解了大学教材里的启动流程了

UEFI

bootloader

加载 kernel 和 initramfs

kernel 启动

kernel 使用 initramfs 作为临时 root

initramfs 找到真正 rootfs

UEFI

理解 UEFI,我们需要先理解 BIOS

BIOS 是在主板的硬件上,预先烧录好的一段程序(这个程序本身是软件)

严谨的说,UEFI 就是 BIOS 的现代形态,是两种不同的固件规范。但是在日常生活中,我们说刷 BIOS,进 BIOS,实际上进入的是 UEFI

UEFI 从 FAT 分区读取引导文件(如 grubx64.efi)

它的职责:找到并加载 bootloader 到内存里

要点:在 FAT 分区里找

bootloader

grubx64.efi 这个引导文件(可执行程序),就是 bootloader

bootloader 有好几种,常见的是:

(1)

如果只装 Windows,是叫做 bootmgfw.efi。

(2)

GRUB。GNU 项目的默认,大多 Linux 发行版走这一个,功能强大而复杂,支持双系统。

(3)

systemd-boot。systemd 项目发起。简洁、轻量。

进入 Windows 系统之后,你是看不到 efi 文件的,因为 Windows 相当于只挂载了硬盘的一部分(NTFS 那部分),而 efi 是在那个 FAT32 里面,大概几百 MB 的样子的分区

Linux 同理,/挂载的是 ext4 那个分区,而你的 efi 是在 FAT32 分区里,除非用 mount 重新挂载 /dev/sda1这种

它的职责:读取磁盘上的内核文件 vmlinuz,把内核加载到内存 RAM

/boot 目录与挂载

实际上,内核在 /boot/ 下面

问题:为什么 bootloader 也在 boot 下面?它不是在另一个分区吗,那个 ESP 分区,FAT32

解答:

bootloader 不是挂载到 /boot/ 下的,那样确实会盖掉内核

实际上我们做了一个操作:

先把内核复制到 tmp,挂载 ESP 分区,然后把内核和 initramfs 复制到 ESP 分区里(因为 rootfs 镜像里内核先落到了 ext4 的 /boot,挂 ESP 会盖住它)

这种是我们通过 systemd-boot 启动的常见操作

还有一种:把 ESP 挂载到 /boot/efi(GRUB 自己能读 ext4)

这种的 /boot/ 是来源于 ext4 分区里的,因为挂 ESP 在 /boot/efi,所以不会覆盖 /boot

/dev 是什么?

dev 是 device 的缩写

/dev 目录是 Linux 中的一个虚拟目录,用来代表“可读写的存储设备”,块设备文件

/dev/sr0 光驱、ISO 镜像

/dev/vda VirtIO 虚拟磁盘

/dev/sda SATA/SCSI/USB 磁盘

/dev/tty 当前终端

/dev/null

/dev/random

关键命令:lsblk -o NAME,MODEL,SIZE,TYPE,FSTYPE,MOUNTPOINTS

/ 的挂载 与 硬盘分区

我们之前也提到了

一个物理硬盘一般分两个区:

systemd-boot(我这次用的):

/dev/sda       整块硬盘
/dev/sda1      EFI 分区(FAT32,挂到 /boot,内核和 initramfs 也在这里面)
/dev/sda2      Linux 根分区

systemd-boot 只认 FAT32,所以内核和 .efi 文件必须一起挤在 ESP 上

GRUB 方案(提一嘴):

/dev/sda1      EFI 分区(FAT32,挂到 /boot/efi,只放 grubx64.efi)
/dev/sda2      /boot 分区(ext4,放内核和 initramfs)
/dev/sda3      Linux 根分区

GRUB 内置了 ext4 驱动,能从 ext4 分区读内核,所以多一个 /boot 分区

Linux 就是把根分区(对应上面的 sda2 或 sda3)挂载到/

鸡和蛋的问题是:

既然 / 是从 /dev/sda3 挂载来的,那 /dev/sda3 又在 / 里面

这是因为 /dev/sda3 是你进入系统后看到的路径,它启动时,看到的是 UUID,然后将 UUID=某个值的分区,当作 /

即 / 是某个块设备里的文件系统被挂载后的视图

findmnt 查看 / 到底来自哪个设备

Windows 和 Linux 的区别是,多根系统和单根系统

Windows 就是把有 Windows 的那个分区找到,当作 C。其他分区,自动挂载到 D 之类的。

Linux 就是 Linux 内核在的那个分区,当作 / ,然后其他分区,继续挂载到 / 下面(不一定是/mnt 下面,任何位置都行,mnt 只是为了表明,就是外挂的)

mount 时,原目录的内容会被盖住

文件系统

一个分区本质上是一大段连续的存储空间

区域本身只是“裸空间”,你需要看到文件/文件夹,就需要在上面创建文件系统

关键命令:mkfs.ext4 /dev/sdb1

ext4 是最经典、稳定、通用,Linux 默认常见选择

btrfs 支持快照、子卷、校验、压缩等高级功能

xfs 大文件、大吞吐场景强,服务器常用

内核加载

在已经启动好的 Linux 里,内核就在:

/boot/vmlinuz-xxx

(即使是物理机,大概率也是叫 vmlinuz,z 表示 gzip,即压缩过,vm 表示虚拟内存技术,当时的革命性技术)

内核和普通程序是一样的,都是 ELF 文件,运行时都在内存,磁盘上都有一个对应的文件

区别是,内核常驻内存,内核由 bootloader 启动

如何找到内核:

/boot/loader/entries/arch.conf 是 systemd-boot 的一个启动项配置文件

UEFI 只知道先启动 BOOTAA64.EFI,也就是 systemd-boot,接下来要靠 arch.conf 告诉它:(1)加载哪个 Linux 内核(2)加载哪个 initramfs(3)内核启动时要带哪些参数

例如:

ini
title Arch Linux ARM
linux /Image
initrd /initramfs-linux.img
options root=/dev/vda2 rw console=ttyAMA0,115200 console=tty0

/Image 这里路径 systemd-boot 里的路径不是看 Linux 运行后的挂载点,而是看 ESP 分区内部的路径

即,我们之前提到了,你的内核实际上是拷贝到 ESP 里了,所以这里不管你怎么挂载 ESP,都是 /Image

bootloader 不知道 /boot 还是 /efi,它只知道 ESP 分区

/boot/loader/loader.conf 控制 systemd-boot 自己

initramfs

init - ram - fs

一个很小的临时根文件系统,被 bootloader 和内核一起加载到内存里

帮内核完成“找到真正根分区”的前置工作(真实根分区可能很复杂,在 NVMe 盘上,被加密了等等)

先用 initramfs 作为临时 /,加载必要模块、识别磁盘,最后切换到真正的根文件系统

这叫做 switch_root

mkinitcpio

mkinitcpio 把“内核启动早期需要的驱动、脚本、工具”打包成 initramfs-linux.img

我这次安装里没有手动跑 mkinitcpio,因为 Arch Linux ARM rootfs 已经自带了

HOOK

内核已经 Load 进入 RAM 之后,它需要加载 rootfs

它需要读取 ext4 的磁盘,但是磁盘驱动本身,在/lib/modules 里,这又是一个鸡蛋问题

所以此时,内核通过加载 initramfs 来解决鸡蛋问题

mkinitcpio 生成 initramfs 的过程,是按照 HOOK 顺序执行

工种按顺序进场:

  1. base → 搭脚手架(放 BusyBox、基本 shell 工具)
  2. udev → 接水电(放设备探测工具)
  3. autodetect → 量尺寸(检测当前硬件,只打包需要的驱动,缩小体积)
  4. modconf → 搬材料(放额外配置文件)
  5. block → 铺管道(放磁盘控制器驱动,比如 virtio_blk.ko)
  6. filesystems → 铺地板(放文件系统驱动,比如 ext4.ko)
  7. keyboard → 装门锁(放键盘驱动,如果你要用 LUKS 密码解锁磁盘)
  8. fsck → 验收(放 fsck 工具检查文件系统)

全部装好 → 打包成 initramfs → 完成

/etc/fstab

Linux 开机时的自动挂载表

哪个分区挂到 /

哪个分区挂到 /boot

用什么文件系统

bootloader 也会知道 rootfs 的 UUID

chroot bootctl

目标 Arch 被挂在/mnt

chroot /mnt /bin/bash 把 /mnt 暂时当成新的 /

bootctl --esp-path=/boot install

把 systemd-boot 的 EFI 程序复制到 ESP

/boot/EFI/BOOT/BOOTAA64.EFI 是 ARM64 UEFI 的默认 fallback 启动路径。即使 bootctl 没法写 NVRAM 启动项,UTM 的 UEFI 也能从这里找到 bootloader