
一台 Linux 服务器在机房断电后重新通电你 SSH 进去发现一切正常另一台服务器同样断电后却卡在某个画面远程连不上、串口也没有任何输出。同样都是 Linux为什么一台能恢复服务另一台要人工介入这时候不少人的第一反应是“硬件坏了”但真正排查下来有相当概率是启动链路里某个环节出了问题。能完整回答“Linux 是如何启动的”的人面对这类问题不会慌。因为这本质上不是一条看不见摸不着的“玄学链路”而是可以按阶段拆解、按日志定位、按配置修复的确定性过程。无论你是刚入行的运维、准备面试的后端开发还是做嵌入式或容器平台的工程师今天这篇文章我都会按实际启动顺序讲清楚 Linux 从按下电源键到出现登录提示符的完整过程并给出每个阶段可以用到的验证命令和排障思路。1. 为什么要理解 Linux 启动过程先抛一个判断启动过程不是 Linux 的冷门知识而是系统管理员和 DevOps 工程师排障时的第一层地图。原因很简单服务器一旦无法启动所有上层应用、监控、自动化流程通通不可用你必须直接从“开机到用户态”这条链路里找问题。而这条链路的每一段都有它独立的日志、配置文件和工具链。举一个高频场景你给一台机器新加了内核参数重启后系统卡在挂载根文件系统之前。如果不了解启动过程你可能反复重装系统了解启动过程后你会先判断是不是内核参数写错然后从 GRUB 菜单进入 emergency 模式把参数改回来。再比如面试中经常被问到“Linux 启动过程包括哪些阶段”考察的其实不是背诵能力而是你有没有真正理解 BIOS/UEFI、引导加载程序、内核、init 系统这几层分别承担什么职责、谁会先于谁运行、各自的日志去哪里看。这些问题在实际排障中一定会碰到。这篇文章不会只列阶段名称。我会从固件到用户态把每一层的核心机制、关键文件、常用命令和最容易踩的坑都讲清楚。读完你不仅能说清楚 Linux 启动流程还能照着做一次启动耗时分析甚至解决一次启动卡住的问题。2. Linux 启动的整体阶段划分Linux 的启动过程可以按“谁在运行、谁负责接力”的方式拆成四个大阶段阶段运行主体核心职责典型组件常见日志/查看方式固件阶段BIOS/UEFI硬件自检、选择启动设备BIOS / UEFI 固件厂商固件界面、串口输出引导加载阶段引导加载程序加载内核和初始化内存盘GRUB2GRUB 菜单、grub.cfg内核初始化阶段Linux 内核驱动初始化、挂载根文件系统vmlinuz、initramfsdmesg、内核日志用户态初始化阶段PID 1 进程启动系统服务、完成多用户环境systemd / SysVinitsystemctl、journalctl这四段是层层递进的关系。前一个阶段没有完成后一个阶段根本不会开始。很多所谓“系统起不来”的问题其实是在某一棒交接时断掉的。比如内核已经起来但找不到根文件系统和 systemd 无法启动某个关键服务表现上都是“无法正常进入系统”但排查方向完全不同。总览完这四个阶段后从下一节开始我们逐层深入。每一层我都会告诉你它到底做了什么、有没有办法看到它的输出、出了问题该看哪里。3. 阶段一固件与硬件初始化 BIOS/UEFI3.1 BIOS 和 UEFI 的区别传统 BIOS 和现代 UEFI 是 Linux 启动的第一棒。很多人认为它们只是“一个设置界面”实际上它们负责的是最底层的硬件初始化以及决定“去找哪一个设备上的引导程序”。传统 BIOS 的工作方式比较古老它做完全局硬件自检后按照设定好的启动顺序逐个去读取启动设备第一个扇区的内容然后跳转执行。它读取磁盘用的是传统 MBR 分区表受到 2TB 磁盘上限和 4 个主分区限制。UEFI 则是现代系统的主场。它本身就是一个小型操作系统支持从 FAT 格式的 EFI 系统分区ESP中读取.efi引导文件支持 GPT 分区表磁盘容量限制小得多。更重要的是UEFI 引入了安全启动Secure Boot机制只允许加载经过签名的引导程序这在某些服务器发行版上默认开启。从运维直觉上要知道自己用的是哪种方式可以看磁盘分区表# 检查磁盘分区表类型gpt 对应 UEFI 启动dos 则可能是传统 BIOS lsblk -o NAME,PARTTYPENAME,SIZE sudo fdisk -l /dev/sda | head -20如果看到/dev/sda1之类的小分区且类型是EFI System基本上就是 UEFI 引导。3.2 固件阶段最容易卡住的现象固件阶段虽然没有 Linux 的日志但它的表现通常很有特征开机后一直停留在品牌 Logo 界面或者只有一个光标在闪又或者直接报“No Boot Device Found”。遇到这种情况先不要急着怀疑 Linux 系统坏了。优先检查BIOS/UEFI 中能否识别到硬盘或 NVMe 盘启动顺序是否正确是否被重置成了 U 盘启动是否开启了 Secure Boot 但引导文件签名不匹配服务器是否开启了 RAID 卡需要先在 RAID 配置界面里确认逻辑盘状态。从实际经验看固件阶段排障的正确姿势是“先看硬件配置界面再查启动顺序最后再怀疑引导程序”。因为固件阶段是一个封闭环境Linux 看不到这里的日志能依靠的就是硬件厂商的界面输出和串口控制台。4. 阶段二引导加载程序 GRUB4.1 GRUB 的核心职责固件阶段完成后控制权交给引导加载程序。绝大多数主流 Linux 发行版使用的是 GRUB2。GRUB 做的事情可以拆成三步找一个能被它识别的文件系统加载自己的配置根据配置展示启动菜单或者直接读取默认内核加载内核文件vmlinuz和初始化内存盘initramfs把启动参数传给内核。换句话说GRUB 是 Linux 内核与固件之间的翻译官。它不负责“启动 Linux”而是负责“找到 Linux 内核并把它加载起来”。这也是为什么很多人修改了/etc/default/grub后必须重新生成 GRUB 配置因为实际生效的是/boot/grub2/grub.cfg或/boot/grub/grub.cfg。4.2 GRUB 配置实例如果你用的是 RHEL、CentOS、RockyLinuxGRUB 主配置通常生成在/boot/grub2/grub.cfg。我们一般改动的是/etc/default/grub# 文件路径/etc/default/grub GRUB_TIMEOUT5 GRUB_DISTRIBUTOR$(sed s, release .*$,,g /etc/system-release) GRUB_DEFAULTsaved GRUB_DISABLE_SUBMENUtrue GRUB_TERMINAL_OUTPUTconsole GRUB_CMDLINE_LINUXcrashkernelauto rhgb quiet GRUB_DISABLE_RECOVERYtrue这里的GRUB_CMDLINE_LINUX就是内核启动参数。排障时经常要调整这行比如去掉rhgb quiet才能看到内核的滚动输出或者追加systemd.unitemergency.target进入紧急模式。修改完要重新生成配置并更新默认启动项# RHEL/CentOS 系 sudo grub2-mkconfig -o /boot/grub2/grub.cfg # Debian/Ubuntu 系 sudo update-grub4.3 GRUB 出现故障时的处理GRUB 阶段最经典的故障是开机进入grub rescue提示符。这通常意味着 GRUB 核心模块或者/boot下的文件缺失、被误删或者磁盘分区发生了变化。grub rescue环境功能非常有限它只有几个内建命令。常见的抢救思路是手动指定 prefix 和 root再加载正常模块set root(hd0,msdos1) set prefix(hd0,msdos1)/boot/grub insmod normal normal上面的(hd0,msdos1)要换成你自己的分区。这里的hd0表示第一块磁盘msdos1表示 MBR 分区表的第一个分区。如果能进入正常 GRUB 菜单再在系统内重新安装 GRUB 到磁盘。如果 GRUB 配置或模块损坏严重通常需要借助 Live CD 或安装盘 chroot 进入原系统修复。GRUB 阶段排障的关键判断是如果你能看到 GRUB 菜单但选内核后卡住那是内核或 initramfs 的问题如果你根本看不到菜单那问题在 GRUB 本身。5. 阶段三内核初始化5.1 内核文件与 initramfs 的作用当 GRUB 把内核和 initramfs 加载进内存后Linux 内核开始接管。这里涉及两个文件vmlinuz-xxx压缩过的内核镜像initramfs-xxx.img初始化内存文件系统它是一个包含必要驱动和工具的临时根文件系统。为什么要先挂一个临时根文件系统因为内核一开始不一定认识你的根分区。比如根文件系统在 LVM 卷上或者必须由特定 NVMe 驱动、RAID 驱动才能访问这些驱动如果直接编译进内核内核会变得很大。initramfs 解决的就是“先加载驱动再挂真实根文件系统”的问题。查看当前系统使用的内核和 initramfsuname -r ls -lh /boot/vmlinuz-$(uname -r) ls -lh /boot/initramfs-$(uname -r).img5.2 内核启动阶段到底做了什么以内核视角看启动阶段做的事情大致包括解压自身建立最初的内存管理结构枚举总线上的硬件设备加载必要的驱动程序挂载 initramfs执行其中/init脚本根据内核参数找到真实根文件系统把根文件系统切换为真实磁盘上的根执行根文件系统上的第一个用户态程序即/sbin/init。这一步最常出问题的位置是“找不到根文件系统”。对应内核日志里会看到类似Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)。出现这种提示时不要怀疑 GRUB而是要看内核参数里root是否正确、磁盘驱动是否缺失、initramfs 是否需要重新生成。5.3 如何查看内核阶段日志内核启动完成后的日志可以用dmesg查看。如果你想看每次开机后的内核环形缓冲区内容它保存在内存里必要时也可以写入文件# 查看内核日志关注 hardware、driver、mount 相关 dmesg | grep -i error\|fail\|usb\|nvme\|sda | head -50 # 如果 dmesg 被普通用户限制加 sudo sudo dmesg -T | tail -100这里要区分两个概念dmesg显示的是内核环形缓冲区反映的是内核初始化期间的硬件和驱动信息而journalctl可以看到完整用户态日志。很多启动问题需要通过两者配合才能定位。从实践上看当你怀疑是内核阶段问题可以临时去掉内核参数里的rhgb quiet这样开机时屏幕上会直接滚动显示内核日志。这个技巧在服务器本地排障时非常常用。6. 阶段四systemd 与用户态服务启动6.1 PID 1 的交接内核完成根文件系统切换后会执行根文件系统上的第一个用户态进程。在主流的现代 Linux 里这个进程就是 systemdPID 永远是 1。之所以强调 PID 1是因为它和普通进程不同它负责拉起整个用户态服务树并且直接或间接成为所有其他进程的祖先进程。如果 PID 1 挂掉内核就会 panic。查看当前系统的 PID 1ps -p 1 -o pid,comm,cmd从材料看现在绝大多数 Linux 发行版都默认使用 systemd但如果你在做嵌入式Linux项目或维护老系统可能会遇到 SysVinit 风格。SysVinit 的启动方式依赖/etc/inittab和/etc/rc.d/rc*.d里的脚本systemd 则全面转向了 target、unit 和并行启动。6.2 target 与依赖关系systemd 里没有传统意义上的“运行级别 3、5”取而代之的是 target。一个 target 可以理解为一组服务的集合。最常见的有multi-user.target对应传统多用户命令行模式graphical.target多用户加图形界面rescue.target单用户模式用于修复emergency.target紧急模式只挂载根文件系统适合底层修复。查看默认启动 targetsystemctl get-default修改默认启动 target比如从图形界面切换为命令行或者反过来sudo systemctl set-default multi-user.target sudo systemctl set-default graphical.targetsystemd 在启动时会根据依赖关系并行拉起服务。每个服务都由一个 unit 文件定义。一个典型的 unit 文件长这样# 文件路径/etc/systemd/system/my-demo.service [Unit] DescriptionMy Demo Service Afternetwork-online.target Wantsnetwork-online.target [Service] Typesimple ExecStart/usr/local/bin/my-demo Restarton-failure Usermyapp [Install] WantedBymulti-user.target其中After表示在 network-online.target 之后启动但它不等于依赖它真正表达依赖的是Wants或Requires。要注意 systemd 并不保证After里的服务一定先完成只是“尽量排序”这也是新手常误解的地方。6.3 分析启动耗时systemd 提供了一组非常实用的启动性能分析命令。我们可以用它们定位“启动到底慢在哪里”# 查看总启动耗时 systemd-analyze # 查看每个服务的启动耗时排名 systemd-analyze blame | head -20 # 生成 SVG 启动过程图虽然我默认不写绘图但命令可以用 sudo systemd-analyze plot boot-plot.svg # 查看关键事件时间链 systemd-analyze critical-chain典型输出里systemd-analyze会分成 firmware、loader、kernel、userspace 四段时间。如果 firmware 或 loader 时间占了大头那就是固件或 GRUB 层的问题如果 userspace 时间很长就去blame里看哪个服务拖了后腿。这也呼应了本文的核心思路启动过程中的每一段都有它自己的“计时器”。系统启动慢并不是笼统的“开机慢”而是能具体到是内核慢、某个服务等待网络慢还是某个自定义 unit 脚本里 sleep 太久。6.4 如何追踪用户态服务启动日志systemd 管理下的服务日志都集中在 journal 里# 查看本次启动的日志 journalctl -b # 查看某个服务的启动日志 journalctl -u sshd -b # 只查看错误级别 journalctl -p err -b如果某个服务启动失败导致不能进入系统开机时会进入 fallback 状态。你可以通过 systemctl 查看失败服务systemctl --failed这个命令列出的就是本次启动中失败的 unit。很多“系统起不来”的真相最后就是systemctl --failed里某个服务报了一个配置路径不存在或者权限不对。7. 常见启动问题与排查方法启动问题虽然种类多但按阶段分类后排查路径非常清晰。下面用表格汇总几个高频问题。问题现象可能原因排查方式解决方案开机卡在 BIOS/UEFI Logo磁盘识别失败、启动顺序错误进入固件界面查看磁盘和 Boot Order修复 RAID 状态调整启动顺序进入grub rescueGRUB 模块或 /boot 文件丢失手动设置 root、prefix 加载 normal修复 GRUB或进 LiveCD 重装 GRUB出现Kernel panic - VFS unable to mount root fsroot 参数错误、磁盘驱动缺失查看内核参数和 dmesg修改 root 参数重新生成 initramfs开机后黑屏无登录界面graphical.target 相关服务失败systemctl get-default切到 multi-user.target修复显示管理器或切换 target服务一直显示 activating(auto-start)服务依赖网络或磁盘等待超时systemctl status 服务名journalctl -u 服务名调整 After/Requires 依赖优化服务配置修改内核参数后无法启动参数写错或模块冲突在 GRUB 菜单按 e 临时编辑启动项移除错误参数后重新生成 grub.cfg私有自定义 unit 启动失败路径错误、权限不足、Type 不匹配systemctl status、journalctl -u修正 ExecStart 路径和文件权限一个非常实用的临时启动技巧是在 GRUB 菜单界面选中内核行按e编辑启动参数。比如要进入 emergency.target可以在linux那一行的末尾追加systemd.unitemergency.target然后按 CtrlX 启动。这比修改配置文件更快适合一次性排障。如果是内核阶段根本起不来优先用dmesg或者临时去掉 quiet 参数看内核输出。如果是用户态起不来优先用systemctl --failed和journalctl。这个先后顺序几乎是固定套路。8. 最佳实践与工程建议理解了启动过程更重要的是把它应用到真实的系统维护和项目交付中。下面这些建议来自比较常见的线上实践能帮你少踩很多坑。8.1 内核参数尽量少改改前先备份启动参数是一个典型的“高影响小改动”区域。很多人在/etc/default/grub里随手加入一串参数后重启系统直接起不来。建议每次修改前先备份sudo cp /etc/default/grub /etc/default/grub.bak.$(date %F)修改后先执行grub2-mkconfig或update-grub生成新配置再重启。在生产环境最好先在测试机验证。8.2 学会使用 GRUB 临时编辑别急着重装只要 GRUB 菜单还能出现系统就没有完全死亡。按e进入临时编辑可以临时更换内核参数、指定 systemd target甚至指向旧内核。这种“临时启动”方式不写入磁盘非常适合试错。确认参数可用后再写入配置文件。8.3 定期检查内核与 initramfs 是否匹配如果你手动升级过内核或者执行过重新打包 initramfs 的操作要注意内核版本和 initramfs 文件是否能对上。更新内核后没有重新生成 initramfs是启动失败的高频原因之一。在 RPM/DEB 系发行版上正常更新会自动执行脚本但如果手动操作过/boot目录就需要留意了。8.4 记录并对比每次启动耗时把启动耗时变成可追踪的指标而不是“感觉变慢了”。可以写一个简单的脚本每次开机后把systemd-analyze的耗时记录到/var/log/boot-time.log#!/bin/bash # 文件路径/usr/local/bin/boot-time-log.sh { echo $(date) systemd-analyze systemd-analyze blame | head -10 } /var/log/boot-time.log然后通过 systemd timer 或 rc.local 在开机后执行。一段时间后你就能看出启动耗时的变化趋势而不是等到卡死才去排查。8.5 systemd unit 编写要遵循最小依赖原则自定义服务时不要为了“保险”写一堆 Wants/After 依赖过度依赖往往会拖慢启动。依赖越多systemd 的调度越复杂出问题的可能性也越大。建议只声明真正需要的依赖并且优先使用Wants而不是Requires避免一个网络依赖把整个服务树拖挂。8.6 安全的排障氛围先备份、再操作、记录每一步这一点专门提醒从事运维或平台开发的同学。在生产环境排查时先确认当前会话不会因为重启丢失再修改配置。任何时候执行 reboot 前都要检查/etc/default/grub、/etc/fstab、关键 unit 文件有没有被改动过。如果有多台机器先在测试环境复现问题再上生产。9. 总结与后续学习方向到这里一条完整的 Linux 启动链路已经梳理清楚了从 BIOS/UEFI 固件检测硬件到 GRUB 找到并加载内核再到内核通过 initramfs 识别根文件系统最后交给 systemd 拉起整个用户态服务树。你只需要记住每一阶段的交接标志固件阶段找启动设备GRUB 阶段加载内核和 initramfs内核阶段挂载根文件系统执行 PID 1systemd 阶段按 target 和依赖关系启动服务。哪个阶段出问题就在哪个阶段用对应的工具去看。内核之前的阶段重点看硬件界面和 GRUB 菜单内核阶段重点看dmesg用户态阶段重点看systemctl --failed和journalctl。能把问题定位到具体阶段你就已经解决了一大半。下一步建议你亲自做两件事一是查看自己这台机器的systemd-analyze输出看看四个阶段的时间分配二是在测试机上刻意制造一次引导故障比如修改默认内核参数观察启动失败后如何用 GRUB 临时编辑恢复。这种模拟排障比看十篇文章都更有用。如果你对启动过程中的某一块特别感兴趣比如 GRUB 是如何从磁盘定位内核的、initramfs 内部到底有什么、或者 systemd 的单元依赖图怎么分析都可以顺着这篇文章的方向继续深入。掌握启动过程后你会发现 Linux 的很多“神秘现象”本质上都只是某一阶段交接时的一个配置细节。