2、Linux启动流程详解:BIOS/UEFI阶段、Bootloader(GRUB)阶段、内核初始化、systemd启动、用户空间初始化

说实话,搞了这么多年系统运维,我见过太多启动失败的案例。有一次凌晨三点被叫起来,说某台核心数据库服务器起不来了。我远程一看,屏幕停在GRUB界面,死活进不去系统。后来发现是/boot分区写满了,内核更新后旧内核没清理,GRUB配置直接崩了。

从那以后,我养成了一个习惯:每次排查启动问题,先在心里过一遍完整的启动流程。你想想看,如果连系统是怎么从按下电源键到出现登录提示符的都不清楚,那分析启动日志基本就是瞎蒙。

这一章,我就带你把这五个阶段彻底捋一遍。嗯,咱们从最开始的硬件自检说起。

2.1 BIOS/UEFI阶段:硬件自检与启动设备选择

按下电源键,CPU通电,第一件事就是去执行固化在主板ROM里的固件代码。这个固件,要么是传统的BIOS,要么是现代的UEFI。

核心区别一句话概括:BIOS用16位实模式,只能读MBR(主引导记录),最大支持2TB磁盘;UEFI用32/64位保护模式,能读GPT分区表,支持2TB以上磁盘,而且自带文件系统驱动(FAT32)。

我个人建议,现在新部署的服务器,能用UEFI就别用BIOS。为什么?因为UEFI的启动速度更快,安全性更高(Secure Boot),而且日志更详细。我在项目中遇到过一台老机器,BIOS下挂载了4块4TB硬盘做RAID,结果BIOS自检阶段就花了将近3分钟,换成UEFI后直接缩短到30秒。

特性 BIOS UEFI
运行模式 16位实模式 32/64位保护模式
磁盘分区表 MBR(最多4个主分区) GPT(最多128个分区)
磁盘容量限制 2TB 9.4ZB(理论值)
启动管理器 无,直接读MBR 有,EFI系统分区(ESP)
安全启动 不支持 支持(Secure Boot)
日志输出 屏幕显示,无持久化 可写入UEFI变量或日志文件

这个阶段的关键日志,其实你平时不太容易直接抓到。BIOS阶段的输出通常只显示在屏幕上,一闪而过。但UEFI阶段,有些固件会把POST(Power-On Self-Test)日志写入NVRAM,你可以通过efibootmgr命令查看启动顺序和变量。

小技巧:如果你怀疑是硬件自检阶段的问题,可以试试在GRUB启动时按Esc或F2进入固件设置,把POST的详细输出模式打开。有些服务器(比如Dell PowerEdge)的iDRAC里也能抓到完整的POST日志。

2.2 Bootloader(GRUB)阶段:引导加载与内核选择

固件自检通过后,它会根据配置的启动顺序,去读取启动设备上的引导加载程序。在Linux世界里,GRUB(GRand Unified Bootloader)是绝对的主流。

GRUB的核心任务就两个:加载内核到内存,然后跳转执行。但这个过程其实分了好几步。

  1. 第一阶段(boot.img): 写在MBR或GPT的专用分区里,很小(512字节)。它的唯一任务就是加载下一阶段的core.img。
  2. 第二阶段(core.img): 包含文件系统驱动,能识别/boot分区。它去读取/boot/grub/grub.cfg配置文件。
  3. 第三阶段(grub.cfg解析): 显示菜单,等待用户选择(或超时自动选择),然后加载指定的内核(vmlinuz)和initramfs(初始内存文件系统)。

我曾经踩过的坑:有一次升级内核后,忘了运行grub2-mkconfig,结果重启后GRUB菜单里还是旧内核。更惨的是,旧内核因为某个驱动不兼容,直接panic了。所以,每次更新内核后,务必执行:grub2-mkconfig -o /boot/grub2/grub.cfg(CentOS/RHEL)或update-grub(Debian/Ubuntu)。

GRUB阶段的日志,你可以在启动时按e键编辑启动项,然后删掉quiet和rhgb参数,加上verbose。这样GRUB会把加载内核的详细过程打印到屏幕上。不过这些日志不会持久化,所以你得眼疾手快,或者用手机拍下来。

2.3 内核初始化阶段:从压缩内核到进程1

GRUB把内核和initramfs加载到内存后,就把控制权交给了内核。内核开始自解压,然后进行一系列硬件初始化。

这个阶段,说白了就是内核在“认亲”——它要识别CPU、内存、磁盘、网卡、中断控制器……所有硬件。内核会调用各个子系统的初始化函数,比如:

  • 中断控制器初始化: 设置IDT(中断描述符表)
  • 内存管理初始化: 建立页表,初始化伙伴系统
  • 调度器初始化: 创建idle进程和init进程
  • 设备驱动初始化: 枚举PCI设备,加载驱动
  • 文件系统初始化: 挂载根文件系统(从initramfs切换到真正的根)

这里有个关键点:initramfs。它是一个小型的根文件系统,包含了必要的驱动和工具,用来挂载真正的根文件系统。为什么需要它?因为内核本身不包含所有文件系统驱动(比如ext4、xfs、btrfs),这些驱动通常以模块形式存在。initramfs就是用来加载这些模块的。

查看内核启动日志的命令:

# 查看当前启动的日志(最常用)
dmesg

# 查看上一次启动的日志(如果系统还能起来)
journalctl -k -b -1

# 查看特定阶段的日志(比如PCI设备枚举)
dmesg | grep -i pci

我个人习惯,在排查内核初始化问题时,会重点关注dmesg输出里的这几个关键词:Kernel panic、Oops、BUG、Unable to handle kernel、Call Trace。一旦出现这些,基本就是内核崩溃了,得从驱动或硬件兼容性上找原因。

2.4 systemd启动阶段:并行化服务启动

内核初始化完成后,会启动第一个用户空间进程——PID 1。在现代Linux发行版里,这个PID 1就是systemd。

systemd的启动逻辑,和传统的SysV init完全不同。SysV是串行启动,一个接一个跑脚本;systemd是并行启动,能同时拉起多个服务,速度飞快。

systemd启动的核心流程:

  1. 执行默认目标(default.target): 通常是一个符号链接,指向graphical.target(图形界面)或multi-user.target(命令行)。
  2. 解析依赖关系: systemd读取所有.service、.socket、.target等单元文件,构建依赖树。
  3. 并行启动服务: 按照依赖关系,能同时启动的就同时启动。比如网络服务和SSH服务没有依赖关系,就可以一起启动。
  4. 记录启动日志: systemd把所有的启动日志都记录在journal里,你可以用journalctl查看。

分析systemd启动瓶颈的命令:

# 查看每个服务的启动耗时
systemd-analyze blame

# 查看启动过程的依赖图(生成SVG)
systemd-analyze dot | dot -Tsvg > boot_graph.svg

# 查看关键链路上的耗时
systemd-analyze critical-chain

我记得有一次,某台服务器启动特别慢,用systemd-analyze blame一看,有个NetworkManager-wait-online.service竟然花了45秒。说白了,这个服务在等网络完全就绪,但我们的服务器是静态IP,根本不需要等。直接把它mask掉,启动时间从2分钟降到了30秒。

2.5 用户空间初始化阶段:登录提示符与图形界面

systemd启动完所有系统服务后,最后一步就是启动用户空间的服务。这包括:

  • getty服务: 在虚拟终端(tty1-tty6)上启动登录提示符。
  • 显示管理器(如GDM、SDDM、LightDM): 如果默认目标是graphical.target,就会启动图形登录界面。
  • 用户会话启动: 用户登录后,启动用户级别的systemd实例(systemd --user),然后加载用户环境变量、启动桌面环境或窗口管理器。

这个阶段的日志,主要看journalctl -u加上服务名。比如:

# 查看SSH服务的启动日志
journalctl -u sshd.service

# 查看显示管理器的启动日志
journalctl -u gdm.service

# 查看用户会话的日志(以用户ID 1000为例)
journalctl _UID=1000

嗯,这里要注意:用户空间初始化失败,通常不会导致系统崩溃,但会导致你无法登录。比如有一次,我遇到/etc/fstab里挂载了一个不存在的NFS共享,结果remote-fs.target一直等待超时,导致所有依赖它的服务(包括SSH)都启动不了。最后只能进单用户模式修复。

2.6 完整启动流程SVG图

下面这张图,把整个启动流程串起来了。你可以对照着看,每个阶段对应哪些日志来源。

Linux完整启动流程 Linux 完整启动流程(5个阶段) 1. BIOS/UEFI 硬件自检 + 启动设备选择 2. GRUB Bootloader 加载内核 + initramfs 3. 内核初始化 硬件识别 + 驱动加载 读取MBR/ESP 跳转执行 4. systemd 启动 并行启动系统服务 5. 用户空间初始化 登录提示符 / 图形界面 启动 PID 1 (systemd) 启动 default.target 📌 日志来源速查 • 阶段1-2: 屏幕输出 / UEFI变量 • 阶段3: dmesg / journalctl -k • 阶段4: journalctl -u <service> • 阶段5: journalctl _UID=<uid> 核心思路:从硬件 → 固件 → 引导加载程序 → 内核 → 系统服务 → 用户空间 每个阶段都有对应的日志输出,学会定位到具体阶段是分析启动问题的第一步

2.7 本章小结

好了,这一章我们把Linux启动的五个阶段完整过了一遍。你想想看,从按下电源键到出现登录提示符,中间经历了硬件自检、引导加载、内核初始化、systemd并行启动、用户空间初始化。每个阶段都有自己独特的日志输出方式。

我个人觉得,理解这个流程最大的价值在于:当系统启动失败时,你能快速定位到是哪个阶段出了问题。比如:

  • 屏幕黑屏、无任何输出 → 硬件或BIOS/UEFI阶段
  • 停在GRUB菜单或报错 → Bootloader阶段
  • 内核panic或挂起 → 内核初始化阶段
  • 服务启动超时或失败 → systemd阶段
  • 能登录但图形界面起不来 → 用户空间初始化阶段