
按下电源键到看到登录提示中间那几十秒到底发生了什么这个问题看起来基础但真到排障的时候能一口气讲清楚的人真不多。我有一次在生产环境遇到过服务器重启后起不来现场同事盯着屏幕一脸茫然日志刷了一屏又一屏愣是不知道卡在哪一步。最后定位下来是/etc/fstab里一个挂载项的 UUID 写错了系统在等待一个不存在的设备超时后进了 emergency mode。这类问题本身不复杂但前提是你得知道引导过程的每一段在干什么、该看什么日志、去哪修。这篇就把 Linux 引导过程从头到尾拆开讲一遍从固件到内核再到 systemd每一跳的原理、验证命令、常见故障和修复手段都写清楚希望能帮你把这条链路真正串起来。1. 从固件到内核引导链路上的四个关键跳转很多人把引导过程理解成“开机 → BIOS → 系统 → 桌面”这个理解太粗了。实际上从按下电源键到 login 界面中间至少经历了四次职责交接固件 → 引导加载器 → 内核 → init 进程。每次交接都有一个明确的边界搞清楚这些边界排障时才知道问题出在哪一段。1.1 第一跳固件决定从哪里启动按下电源键之后CPU 首先执行的是固件代码。x86 架构下传统 BIOS 会把控制权交给固定在某个地址的代码现代 UEFI 则由固件自身的启动管理器Boot Manager接管。这一步的目标只有一个找到一个可启动的设备并把控制权交给设备上的引导加载器。传统 BIOS 时代启动设备靠 MBR主引导记录识别。MBR 是硬盘第一个扇区512 字节的前 446 字节里面装的是一段很小的引导代码。BIOS 扫描所有设备的 MBR找到合法的引导标志后就直接把控制权交过去。这个过程非常“原始”既没有文件系统的概念也不认识分区格式纯粹是固定地址跳转。UEFI 则完全不同。它引入了ESPEFI System Partition这是一个 FAT 格式的小分区里面存放.efi文件。固件不再傻乎乎地读扇区而是自己附带了一套简单的文件系统驱动直接去 ESP 分区里找EFI/boot/bootx64.efi或者按 NVRAM 里记录的可引导条目来加载。这就是为什么 UEFI 启动方式下硬盘的 GPT 分区表里必须有一个 ESP 分区。你可以用efibootmgr查看当前机器上的 UEFI 启动条目efibootmgr -v如果输出里显示Boot0000* ubuntu HD(1,GPT,...)/File(\EFI\ubuntu\shimx64.efi)说明固件是从 ESP 分区里加载 shimx64.efi一个用于 Secure Boot 链的引导器。能看到这一步至少说明固件阶段是正常的。1.2 第二跳GRUB 的使命是“找到内核并加载它”固件把控制权交给了 GRUB但 GRUB 面对的处境比固件还尴尬MBR 里只有 512 字节而一个完整的 GRUB 远不止这么大。所以 GRUB2 被拆成了两段。boot.img放在 MBR 或 GPT 的 BIOS Boot Partition 里极小职责是定位并加载core.img。core.img放在 MBR 后面的扇区包含了一些基础的文件系统驱动让 GRUB 能读懂 ext4、xfs、btrfs 等常见文件系统然后去/boot/grub2/grub.cfg不同发行版路径略有差异里读取配置。grub.cfg 是 GRUB 的“菜单数据源”它定义了启动菜单里每个内核条目的启动参数。一个典型的条目长这样menuentry Ubuntu { load_video set gfxpayloadkeep insmod gpt insmod ext2 search --no-floppy --fs-uuid --setroot 3f8b8c5e-... linux /vmlinuz-5.15.0-86-generic rootUUID3f8b8c5e-... ro quiet splash initrd /initrd.img-5.15.0-86-generic }这里有两个很关键的指令linux告诉 GRUB 内核镜像文件在哪以及要传给内核的参数。rootUUID...指定了真正的根文件系统所在分区ro表示先以只读方式挂载根quiet splash只是给用户看的启动画面参数。initrd指定 initramfs 镜像。这一步在后面会细说。GRUB 加载内核和 initramfs 到内存后会根据配置跳转到内核入口。到这一步GRUB 的使命就完成了后续它不再参与系统运行。1.3 第三跳内核的“冷启动”与 initramfs 的秘密内核镜像本身是压缩过的文件头里有解压代码所以 GRUB 跳转过去后内核先把自己解压到内存里然后开始初始化各种核心子系统内存管理、调度器、中断、时间、PCI 总线、块设备驱动等。但此时存在一个“先有鸡还是先有蛋”的问题内核要挂载真正的根文件系统就得有对应的磁盘驱动而驱动本身可能就在根文件系统上比如驱动作为内核模块存放。如果内核把所有驱动都编进去内核体积会巨大无比还不灵活。解决方式就是initramfs早期用户空间。它是一个 cpio 格式的压缩镜像里面是一个迷你的根文件系统包含必要的内核模块、udev 规则、lvm 工具、文件系统工具以及一个/init脚本。内核先把这个 initramfs 挂载为临时根然后运行/init在这个临时环境里加载各种驱动、组装 LVM 卷、设置 dm-crypt最终找到真实的根设备再切换到真实根上继续启动。这个过程在内核日志里能看到类似这样的信息[ 1.234567] Unpacking initramfs... [ 2.345678] Freeing initrd memory: 10240K [ 3.456789] EXT4-fs (sda2): mounted filesystem with ordered data mode. Quota mode: none.当看到Freeing initrd memory这行时说明 initramfs 已经完成使命内核对它的内存占用已经释放之后进程切换到真实根并启动 1 号进程。1.4 第四跳systemd 接管用户空间真实根挂载完成后内核会执行/sbin/init。在主流发行版上这个程序就是 systemd它成为系统的1 号进程PID 1。systemd 会读取默认的 target相当于传统 SysVinit 的运行级别然后按照依赖关系启动所有单元。可以用systemctl get-default查看当前默认目标systemctl get-default通常是graphical.target带图形界面或multi-user.target纯命令行。systemd 与传统 init 最大的不同是并行启动它先把整个系统的启动依赖图解析出来然后在不违反依赖顺序的前提下能并行的服务同时启动。这也是为什么现代 Linux 启动速度比 SysVinit 时代快很多。依赖图的起点和终点可以这样看systemctl list-dependencies default.target这个命令会输出一长串依赖树从后往前追就能知道系统启动的完整路径。这一跳之后登录界面出现引导过程结束系统进入正常运维状态。2. 用三个命令看清你自己机器上的真实引导链理论讲完如果不落到自己机器上验证一遍很容易变成“背概念”。我用三个命令帮你把当前系统的引导链“物化”出来每一步都能看到实打实的证据。2.1 先看/boot目录引导链的物证ls -lh /boot输出里你会看到两类核心文件vmlinuz-*内核镜像和initramfs-*.img或initrd.img-*initramfs 镜像。如果有多个版本说明系统里装了多个内核启动菜单里也会有多个条目。这也是“内核升级后启动失败”这类问题的重灾区新内核的 initramfs 生成失败或者分区空间不足导致 initramfs 没写全。还有grub2/或grub/目录里面的grub.cfg就是刚才说的菜单配置。注意这个文件不要手动编辑它是由grub2-mkconfig这类工具自动生成的手动改的内容在下一次重新生成时会被覆盖。2.2 用file命令识别内核和 initramfs 的真实格式file /boot/vmlinuz-$(uname -r) file /boot/initramfs-$(uname -r).img第一条输出类似/boot/vmlinuz-5.15.0-86-generic: Linux kernel x86 boot executable bzImage, version 5.15.0-86-generic, RO-rootFS, swap_dev 0x3, Normal VGA看到了吧bzImage就是 Linux 内核在 x86 平台上的标准镜像格式。第二条输出会让你惊讶/boot/initramfs-5.15.0-86-generic.img: ASCII cpio archive (SVR4 with no CRC)initramfs 本质就是一个 cpio 归档你甚至可以直接解压它进去看mkdir /tmp/initramfs cd /tmp/initramfs zcat /boot/initramfs-$(uname -r).img | cpio -idmv解压后你能看到里面的init脚本、lib/modules/目录、usr/bin里的各种工具。下次你再遇到“为什么能进 initramfs 却起不来系统”的问题就知道进这个临时环境里看什么了。2.3 用journalctl和systemd-analyze还原启动现场引导阶段从固件到 initramfs的日志可以用dmesg或journalctl -k查看内核环形缓冲区journalctl -k -b-b表示本次启动。启动完成后systemd 接管后各服务的启动时间可以用systemd-analyze查看systemd-analyze systemd-analyze blame第一条会输出类似Startup finished in 3.842s (firmware) 4.312s (loader) 1.263s (kernel) 11.572s (userspace) 21.989s分段时间一目了然固件多久、引导加载器多久、内核多久、用户空间多久。如果 userspace 时间异常长问题大概率出在某个 systemd 服务上如果 firmware 时间异常长那就得去检查固件配置了。systemd-analyze blame则按耗时从大到小列出所有服务帮你锁定是哪个服务拖慢了用户空间启动。3. 启动故障排查救援模式里的三类常见死法引导过程每一跳都有对应的坑。我挑三个最常见的故障场景完整走一遍排查和修复的链路。3.1 死法一fstab 写错卡在等待设备超时现象启动过程中卡住屏幕上反复刷类似A start job is running for /dev/disk/by-uuid/xxxx-xxxx (1min 30s / 1min 30s)超时后进入 emergency mode提示Give root password for maintenance。这类问题九成是/etc/fstab里的挂载项出了问题——常见的包括 UUID 写错、设备路径写死、或者挂载了一个不存在的分区。排查链路输入 root 密码进入 emergency mode 后第一件事是重新以读写方式挂载根文件系统因为此时根是只读的mount -o remount,rw /然后检查 fstabcat /etc/fstab对比实际设备信息lsblk -flsblk -f输出里的 UUID 列和 fstab 里的 UUID 一比对问题立刻暴露。比如 fstab 里写的是UUID3f8b8c5e-...但lsblk显示那个分区真实的 UUID 是a1b2c3d4-...那就是 UUID 对不上。修复方法修正或注释掉错误的行。如果只是临时想跳过这个错误挂载点可以直接注释掉那一行重启后系统就能正常进入。为什么 UUID 变了就挂不上UUID 是分区格式化时生成的唯一标识。如果你做过分区重建、格式化、或者从别的机器克隆了系统盘UUID 就可能变化。这也是为什么很多运维老手写 fstab 时坚持用 UUID 而不是设备名如/dev/sda1因为设备名在插入新磁盘、调整硬盘顺序后会漂移UUID 则稳定得多。3.2 死法二GRUB 崩了只剩一个grub提示符现象开机后不进菜单直接掉到 GRUB 命令行或者报error: no such partition、error: unknown filesystem。可能的根源MBR 或引导扇区被覆盖比如装 Windows 后 Linux 引导丢了、grub.cfg 损坏、分区表调整导致 GRUB 找不到/boot。排查链路这种场景下系统还没进入内核journalctl 也救不了你只能在 GRUB 命令行里手动把内核拽起来。步骤如下先在 GRUB 命令行里列出所有磁盘和分区grub ls (hd0) (hd0,gpt1) (hd0,gpt2) (hd0,gpt3)逐个查看哪个分区里有内核grub ls (hd0,gpt2)/找到能列出vmlinuz-*和initramfs-*.img的分区后手动设置启动参数grub set root(hd0,gpt2) grub linux /vmlinuz-5.15.0-86-generic root/dev/sda2 ro grub initrd /initramfs-5.15.0-86-generic.img grub boot这里的root/dev/sda2要换成你实际根分区对应的设备。如果能正常进入系统说明内核和根文件系统都没坏只是 GRUB 配置丢了。接下来重新安装并生成配置grub2-install /dev/sda grub2-mkconfig -o /boot/grub2/grub.cfg不同发行版命令可能有差异Debian/Ubuntu 系是grub-install和update-grubCentOS/RHEL 系是grub2-install和grub2-mkconfig。记不清就敲grub再加 Tab 补全。这里有个易踩的坑如果 GRUB 能进入菜单但选择某个内核条目后直接黑屏或重启往往是内核参数不对而不是 GRUB 本身坏了。在启动菜单里按e编辑条目去掉quiet splash加上nosplash让内核输出完整日志能看到卡在哪一步再对症下药。3.3 死法三内核 panic连根文件系统都找不到现象GRUB 正常加载了内核但内核在挂载根文件系统时崩溃屏幕上出现经典的红字Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)本质原因内核启动了但找不到根设备。常见原因有两个一是 initramfs 缺失或损坏内核没有可用的磁盘驱动二是root参数指向的设备不对。排查链路首先确认 initramfs 文件是否存在且完整ls -lh /boot/initramfs-$(uname -r).img如果文件存在但怀疑损坏可以从系统安装盘或救援镜像启动挂载根文件系统后 chroot 进去重建 initramfsmount /dev/sda2 /mnt mount --bind /proc /mnt/proc mount --bind /sys /mnt/sys mount --bind /dev /mnt/dev chroot /mntchroot 进去后相当于进入了“病系统”的完整文件系统环境此时重新生成 initramfsdracut -f /boot/initramfs-$(uname -r).img $(uname -r)Debian/Ubuntu 系用update-initramfs -u -k all生成完毕后检查是否成功然后exit退出 chroot重启验证。关键逻辑initramfs 里放什么驱动直接决定了内核在这个小小的临时根里能不能看到你的硬盘。如果你用的是某些不常见的 RAID 卡、NVMe 控制器或者 LVM、LUKS 加密盘initramfs 必须包含对应工具和模块。所以“升级内核后系统突然无法启动”的常见场景之一就是新内核生成的 initramfs 里缺了驱动——这种情况下重建 initramfs 往往能解决一大半问题。3.4 排查启动问题的通用思路三个故障走下来可以总结出通用排查顺序确认卡在哪个阶段固件开机 Logo 或厂商界面停留久、GRUB菜单出不来、内核日志滚动后卡住、还是用户空间进度条超时。能看日志就先看日志journalctl -b看本次启动dmesg看内核。进不了系统就用救援介质发行版安装 U 盘基本都有 “Troubleshooting” 或 “Rescue” 模式chroot 后把系统当一个“病人”来诊断。每做一次修改只改一个变量重启验证一次避免多变量互相干扰。4. 启动时间优化从 systemd-analyze 到立竿见影的调整讲完故障再讲优化。引导过程不是懂了就行实际生产环境里“启动越慢 宕机恢复越慢”所以优化启动时间也是运维的必修课。4.1 先量化再动手老话讲“没有数据就没有方向”。先跑一遍systemd-analyze拿基线systemd-analyze输出会拆出 firmware、loader、kernel、userspace 四段耗时。如果 userspace 占了很大比例就用blame看具体是哪个服务在拖systemd-analyze blame典型输出1min 30s dev-disk-by\x2duuid-xxx.device 12.345s postfix.service 5.678s cloud-init-local.service看到dev-disk-by\x2duuid-xxx.device占了 90 秒基本可以断定是等待某个设备超时——就是 3.1 里讲的 fstab 问题。先把这类“等待超时”解决掉启动时间立刻能砍掉一大半。4.2 用 critical-chain 找关键路径blame只能看单个服务耗时但系统启动是并行的真正决定启动总时长的是关键路径。systemd-analyze critical-chain会从启动目标往回追溯找出那条最长的依赖链systemd-analyze critical-chain输出类似graphical.target 14.023s └─multi-user.target 14.023s └─docker.service 10.645s 3.375s └─containerd.service 9.981s 659ms └─network-online.target 9.977s └─NetworkManager-wait-online.service 5.432s 4.544s这里一眼就能看出来NetworkManager-wait-online.service花了 4.5 秒阻塞了后续网络服务的启动。这类服务的意义是“等网络真正就绪”但如果你的服务不依赖网络就绪关掉它是很安全的优化。4.3 三种立竿见影的优化技巧第一禁用不需要的 wait-online 服务systemctl disable NetworkManager-wait-online.service systemctl mask systemd-networkd-wait-online.servicemask比disable更彻底直接把服务“软链接到 /dev/null”即使有服务依赖它也不会被拉起。第二缩短服务默认超时时间systemd 对服务启动有默认超时通常 90 秒。有些服务本来就在正常时间内能起来但你希望它更快失败比如挂载一个经常不在线的目录可以调整全局默认值mkdir -p /etc/systemd/system.conf.d cat /etc/systemd/system.conf.d/default-timeout.conf EOF [Manager] DefaultTimeoutStartSec30s DefaultTimeoutStopSec30s EOF systemctl daemon-reexec第三把不需要开机自启的服务 disable 掉systemctl list-unit-files | grep enabled systemctl disable 服务名这一步要克制先搞清每个 enabled 服务是干什么的再动手。我见过有人为了提速把rsyslog都禁了结果系统出问题完全没有日志可查得不偿失。一个容易被忽略的细节如果你用的是 SSDfirmware那几秒往往不是硬盘慢而是 UEFI 固件在初始化内存和 PCIe 设备。这类时间操作系统管不了需要进固件设置里调整比如关闭没用的自检、启用快速启动。所以启动优化要合理预期不要觉得“优化 10 秒”是虚假承诺速度大头本来就在用户空间。5. 把引导链烂熟于心后排障会有多顺畅最后说点实际体会。把 Linux 引导过程理清楚之后最直接的收益不是“能背出概念”而是排查启动问题时思路完全不一样了。以前遇到电脑进不去系统我的第一反应是“重装吧”。后来在一次服务器维护中我在 GRUB 菜单里按e修改内核参数加上systemd.unitmulti-user.target跳过图形界面机器立刻就能进系统了。那次之后我才意识到很多“系统坏了”的案例本质只是某一跳出了问题而引导链的每一跳都有对应的绕过方式。如果你想真正吃透这块内容我的建议是找一个虚拟机做几次破坏性实验删掉 initramfs、改坏 fstab、用dd覆盖 MBR然后尝试用各种方式把系统救回来。这些实验看起来是“自己给自己找麻烦”但正是这种“主动出故障”的训练能让你在真实故障面前不慌。我到现在还保留着一个习惯凡是排查启动问题先在纸上画出那个盒子里应该发生的完整链路然后按链路逐步排除。固件正常吗GRUB 找到内核了吗内核跑起来了吗initramfs 加载了吗根挂上了吗init 起来了吗每一步都有日志、都有命令、都有验证方法。顺着这条链走绝大部分启动问题都能被定位到一个很小的范围内。希望你读完这篇之后也能把这条“看不见的链路”变成自己的排查地图。下次你的系统卡在某个奇怪的界面时你会感谢现在认真追过引导过程的自己。