linux开机流程详解与剖析
系统初始化
启动过程概述
启动过程像是一个四级的火箭。每一级火箭将系统控制权交给下一级。 https://www.junmajinlong.com/linux/systemd/systemd_bootup/
“第一阶段:UEFI”
Unified Extensible Firmware Interface (UEFI) 统一可扩展固件接口 定义了启动管理器作为 UEFI 规范的一部分。
BIOS和UEFI都是计算机固件类型。许多UEFI固件实现了某种类BIOS 风格的兼容模式而已,不存在UEFI BIOS
说法,UEFI的通用性更强。
原生UEFI启动时,GPT磁盘和EFI FAT32 ESP并不是必要条件。但是在99%的情况下UEFI规范和GPT分区表以及EFI FAT32 ESP系统分区的联系程度相当密切。
可扩展固件接口(EFI)支持的文件系统ESP(EFI system partition)是基于FAT文件系统的,在计算机里用来遵照 UEFI 规范。
当计算机打开电源时,载入UEFI开机自检并初始化部分硬件,启动UEFI固件内的启动管理器。UEFI启动管理器(可视为"启动菜单")依据全局NVRAM变量定义的顺序尝试加载UEFI操作系统启动装载程序。配置缺省时,固件将按分区顺序遍历每个EFI系统分区(标识为"esp",也称"EFI System Partition",为fat32分区),查找并加载UEFI规范的有效的启动装载程序\EFI\BOOT\BOOTx64.EFI
。也可指定磁盘、分区、路径、文件名,比如安装Ubuntu21.10时会自动在UEFI启动菜单插入EFI文件路径\EFI\ubuntu\shimx64.efi
。
UEFI配置
Linux发行版使用efibootmgr工具配置UEFI启动管理器。它会创建一个EFI系统分区(如果不存在此分区),一般500MB左右,使用相应配置将EFI启动装载程序(通常为grub2-efi
)安装到EFI系统分区中的正确路径下,然后调用 efibootmgr添加相应的UEFI启动管理器项指向其启动装载程序(boot loader)。
# 查看UEFI启动管理器菜单信息
efibootmgr -v
UEFI优点
- 能识别GPT分区表,能读取类FAT文件系统
- UEFI+GPT支持18EB的磁盘和128个主分区
- 内置图形驱动,可使用鼠标操作
- 不进入系统前也可支持联网
- 提供了进行启动(例如从远程服务器进行启动)的标准方法
- 允许从已启动的Windows和Linux(
efibootmgr
工具)等系统中修改配置启动管理器
现代基于UEFI系统的流行流程
在现代基于UEFI固件的系统中,shim已经成为目前所有Linux发行版本中必备的一阶bootloader;而传统上我们熟悉的grub2变成了二阶bootloader。
例如在Ubuntu21.10中,UEFI启动管理器菜单项bootloader路径是指向\EFI\ubuntu\shimx64.efi
。 shim的主要工作是启动二阶bootloader,shim会在当前目录下寻找grubx64.efi(/EFI/ubuntu/grubx64.efi)。如果不存在,shim会加载当前目录下的fbx64.efi(/EFI/ubuntu/fbx64.efi)或fallback.efi。
系统完成装机并进行第一次启动时
- 系统完成装机并进行第一次启动的时候,UEFI启动管理器会自动枚举出一个叫做UEFI OS的启动选项,该启动选项将bootloader程序的路径设置为/EFI/boot/bootx64.efi,即一阶bootloader shim。
- bootx64.efi会在当前目录下寻找grubx64.efi(
/EFI/boot/grubx64.efi
)。如果不存在,shim会加载当前目录下的fbx64.efi(或fallback.efi)。 - fallback会枚举/EFI目录下的、除boot子目录以外的所有子目录,并找到第一个BOOTX64.CSV文件,例如
EFI/ubuntu/BOOTX64.CSV
- BOOTX64.CSV暗示了当前路径并记录了二阶bootloader的文件名和准备添加的启动项名称,记录的文件名会被用来创建一个全新的启动选项,该启动选项中的程序路径也会被设定为二阶bootloader所在的绝对路径
/EFI/<二阶bootloader路径>
,例如/EFI/ubuntu/shimx64.efi
。 - fallback程序的最后一个工作就是修改BootOrder变量,将新创建的启动选项设为最优先启动,然后issue warm boot重启系统。
缺省时UEFI启动流程总结: 默认启动选项UEFI OS -> /EFI/boot/bootx64.efi(本质就是/EFI/boot/shimx64.efi) -> /EFI/boot/fbx64.efi -> /EFI/ubuntu/BOOTX64.CSV(记录了自定义启动选项的名称为ubuntu
) -> 自动创建启动选项,并修改BootOrder变量,然后自动执行warm reboot。正常情况下,重启后会进入shim normal boot流程。
系统后续的正常启动时
- 正常情况下,UEFI启动管理器会根据新的BootOrder变量,从新的启动选项(比如上面的
/EFI/ubuntu/shimx64.efi
)启动一阶bootloader。 - 然后,shimx64.efi在当前目录下找到grubx64.efi,实际就是启动GRUB程序,这样就来到了我们熟悉的grub2启动流程里了。
正常UEFI启动流程总结: 启动选项ubuntu -> /EFI/ubuntu/shimx64.efi -> /EFI/ubuntu/grubx64.efi -> /EFI/ubuntu/grub.cfg。 正常情况下,不管系统再经过多少次重启,每次都会走该启动流程,不会再执行shim first boot。
UEFI固件对启动顺序策略
UEFI固件对启动顺序有一个策略:
- Keep original priority
- 如果启用了前者,那么shim first boot在修改完BootOrder变量并重启后,自建的引导选项不会影响实际的启动顺序,会导致系统看起来在不停地warm reboot
- Adjust device priority by bootloader
- 如果启用了后者,就会按照预期在重启后走shim normal boot流程。
目前新的机型都将逐步默认适配为UEFI固件,届时AIS维护的所有UEFI固件都将默认以shim normal boot方式启动,上面提到的其他启动方式都将被淘汰掉。我们熟知的那些<bootloader-id>
,都会以列表的形式存储在AIS提供的UEFI固件中,以确保CentOS、Ubuntu等各式系统都能通过shim normal boot方式启动(是的,每个发行版本都有各自不同的bootloader存放目录:/EFI/centos/等等);相反,如果没有加入到这个列表,就会走shim first boot,但可能会因为Keep original priority策略而导致系统无限重启。
第二阶段:引载加载程序GRUB
引导加载程序GRUB是启动过程的第二阶段,由 UEFI 启动。引导加载程序将系统内核映像和 initrd 映像加载到内存并将控制权交给它们。initrd 映像是根文件系统映像,其支持程度依赖于所使用的引导加载程序。
grub2把引导模块化了,每个驱动一个模块放置在/boot/grub/i386-pc/目录下了。
事实上,操作系统的启动分为两个阶段:引导boot和启动startup。引导阶段开始于打开电源开关,结束于内核初始化完成和 systemd 进程成功运行。启动阶段接管了剩余工作,直到操作系统进入可操作状态。
GRUB2 全称是 GRand Unified BootLoader,Version 2(第二版大一统引导装载程序)。它是目前流行的大部分 Linux 发行版本的主要引导加载程序。GRUB2 是一个用于计算机寻找操作系统内核并加载其到内存的智能程序。 GRUB 能够通过文件 /boot/grub/grub.conf 进行配置。
GRUB的引导流程
/EFI/ubuntu/grubx64.efi
-> /EFI/ubuntu/grub.cfg
-> 在第几个硬盘第几个分区执行/boot/grub/grub.cfg
菜单配置脚本 -> 加载必要模块和驱动(/boot/grub/i386-pc) 并 加载/boot/vmlinuz-*
Linux内核 和 /boot/initrd.img-*
微型Linux文件系统(包含必要命令和驱动)
“第三阶段:迷你 Ubuntu 系统”
迷你 Debian 系统是启动流程的第三阶段,由引导加载程序启动。它会在内存中运行系统内核和根文件系统。这是启动流程的一个可选准备阶段。这个系统通常被称为 initrd 或 initramfs 系统。
/init 程序是内存中的根文件系统上执行的第一个程序。这个程序在用户空间把内核初始化,并把控制权交给下一阶段。
迷你Ubuntu系统流程
kernel + initrd –> rootfs(根切换)–> /sbin/init
注意: /sbin/init -> /lib/systemd/systemd
“第四阶段:常规 Ubuntu 系统”
常规 Debian 系统是启动流程的第四阶段,由迷你 Debian 系统启动。迷你 Debian 系统的内核在此环境下继续运行。根文件系统将由内存切换到实际的硬盘文件系统上。
init 程序是系统执行的第一个程序(PID=1),它启动其它各种程序以完成主引导流程。init 程序的默认路径是 "/sbin/init"(软链接到"/lib/systemd/systemd"),但可通过内核启动参数修改,例如 ”init=/path/to/init_program"。
/lib/systemd/systemd初始化
Systemd 取代了initd,成为系统的第一个进程(PID 等于 1),其他进程都是它的子进程。
根据 Linux 惯例,字母d是守护进程(daemon)的缩写。 Systemd 这个名字的含义,就是它要守护整个系统。
Systemd解决了以前initd串行启动时间长,启动脚本复杂的问题,为系统的启动和管理提供一套完整的解决方案。
初始化流程: sysinit.target(驱动和底层服务)->basic.target(系统级基本服务)->default.target->...->login
其中default.target是一个软链接,一般指向graphical.target(图形界面)或multi-user.target(多用户模式),对应于SysV系统中的『运行级别』阶段。
systemd服务有系统和用户区分;系统(/usr/lib/systemd/system/)、用户(/etc/lib/systemd/user/)
# 查看目前系统的运行级别
ls -l /usr/lib/systemd/system/default.target
ls -l /etc/systemd/system/default.target
systemctl get-default #查询
systemctl set-default multi-user.target #设置
# 查看multi-user.target运行级别的启动服务
ls -l /usr/lib/systemd/system/multi-user.target.wants
# 查看看multi-user.target运行级别的用户定义的systemd类的开机自启动服务
ls -l /etc/systemd/system/multi-user.target.wants
# 查看graphical.target运行级别的启动服务
ls -l /usr/lib/systemd/system/graphical.target.wants
# 设置某个服务开机启动
systemctl enable nginx.service
# 在/etc/systemd/system/multi-user.target.wants/目录下新建一个/usr/lib/systemd/system/nginx.service 文件的链接。