按下电源键的那一瞬间,Linux 系统到底在后台忙些什么?我刚接触运维时,启动过程在我眼里就是个黑匣子:开机,等倒计时,系统自己滚几屏日志,最后弹出登录提示符。真正开始排查启动问题后,我才发现这十几秒其实是由多个独立模块接力完成的,每个模块只负责自己的那一棒,交接不出错,系统才能跑完全程。后来我习惯把整个启动链路拆成“固件 → 引导程序 → 内核 → initramfs → systemd → 登录环境”来看,发现问题定位变得特别快。这篇文章就用“接力赛”这条线索,把 Linux 启动过程从头到尾捋一遍。它适合刚入门 Linux 的运维新手,也适合那些已经会用 systemctl、journalctl,但还没把启动全链路串起来的朋友。
1. 启动接力赛的整体路线:先看清每一棒是谁
1.1 六棒选手的名单
先摆一张“参赛选手名单”。从按下电源键到你能在终端里打字,整个启动过程大致分成六棒。
| 顺序 | 接力选手 | 主要工作 | 交接点 |
|---|---|---|---|
| 第1棒 | BIOS/UEFI 固件 | 硬件自检、初始化基础硬件、选择启动设备 | 把启动控制权交给引导程序 |
| 第2棒 | 引导加载程序(GRUB) | 加载内核镜像和 initramfs 到内存 | 跳转到内核入口点 |
| 第3棒 | 内核 | 解压自身、初始化内存与中断、启动内核线程 | 挂载临时根文件系统 |
| 第4棒 | initramfs 中的早期用户态 | 加载磁盘、文件系统相关驱动,处理加密或 LVM | 切换根到真正的磁盘根分区 |
| 第5棒 | systemd(PID 1) | 并行启动各类服务、挂载文件系统、建立用户态环境 | 启动登录管理器或 getty |
| 第6棒 | 登录环境 | 显示登录界面、进入 shell | 用户输入命令 |
这个表格我建议新手先收藏。后面排查问题的时候,你会发现绕来绕去,最终都会落到这张表里的某一棒上。
这里要理解一个关键点:Linux 启动不是一个大而全的程序从头跑到尾,而是多个独立的、相对松散的组件接力协作。为什么这么设计?因为每一棒都有自己的专长和生命周期。固件不认识 ext4 文件系统,内核一开始不认识你机器的硬件拓扑,systemd 不负责 CPU 寄存器的初始化。让每段程序只干自己最擅长的事,再定义好交接接口,整个系统才能灵活适配千奇百怪的硬件组合。我在带新人时习惯用一句话概括整个流程:先把环境摸清楚,再把内核请出来,内核把硬件调度起来,init 系统再把“用户世界”搭起来。这个顺序是固定的,至少主流 Linux 发行版都是这么走的。
1.2 为什么拆成这么多模块,而不是一个大程序
这个问题值得单独说一下。如果你去翻早期的 Unix 系统设计,会发现“小而独立的组件 + 清晰接口”一直是核心思想。启动过程也是一样:固件、引导程序、内核、用户态初始化程序之间没有强耦合,任何一段都可以独立替换。你现在把 GRUB 换成 systemd-boot,或者把 systemd 换成老式 SysV init,系统照样能启动。正是这种模块化设计,让 Linux 能同时跑在几块钱的嵌入式开发板和几万块钱的服务器上。
对普通使用者来说,这个设计带来的直接好处是排障边界清晰。出了问题,你不需要研究整个系统,只需要先定位是第几棒,再去查对应组件的日志和配置。今天我后面讲的所有内容,本质上都是在帮大家建立这种“分棒定位”的直觉。
2. 第一棒最不起眼但也最容易被忽略:BIOS/UEFI 的硬件交接
2.1 固件在按下电源后到底做了哪几件事
按下电源键,首先跑起来的不是 Linux,而是主板固件芯片里的程序。在传统 BIOS 时代,它主要做三件事:POST 自检、初始化基本硬件、按启动顺序找设备。
POST 这个词很多新手听过但不清楚具体含义,翻译成大白话就是“硬件体检”——电源是否稳定、CPU 是否正常、内存能不能通过读写测试、显卡和键盘有没有响应。体检通过之后,固件才会去读启动设备里的引导程序。如果你在真机上看到开机后卡在厂商 Logo 不动,或者蜂鸣器发出奇怪的报警声,多半就是 POST 阶段没过,硬件层面有问题,这时候你连 GRUB 的影子都看不到,排查方向自然要和系统安装、内核配置区分开。
如果你用的是较新的 UEFI 固件,逻辑顺序类似,但细节差异很大。UEFI 的启动方式是读取 EFI 系统分区(ESP)里的引导程序文件,而不是像传统 BIOS 那样死板地读磁盘第一个扇区。这也导致一个常见问题:传统 BIOS 配合 MBR 分区表,最大只能寻址约 2TB 的磁盘;UEFI 配合 GPT 分区表可以轻松支持大容量磁盘和多个系统引导项。新机器装 Linux 如果分区表选错,装完系统以后就是起不来,这就是第一棒的交接规则没搞清楚。
2.2 启动设备顺序:机房排障的第一个怀疑对象
固件的第三件事是“按启动顺序找设备”。进 BIOS/UEFI 的 Boot Options,你会看到一堆条目:硬盘、U 盘、光驱、网络启动(PXE)。如果顺序不对,比如带着 U 盘启动,机器会优先读 U 盘里的引导程序,而不是硬盘里装好的 Linux 系统。
我在机房排障时经常遇到这样的情况:一台服务器明明昨天还好好的,今天重启后卡在某个界面不动,结果一看,是管理口把启动设备顺序改了,系统根本没进 GRUB,而是尝试从空 U 盘或者 PXE 网络启动。遇到这种情况,最简单的验证方式就是进固件界面看一眼 Boot Order,把硬盘调到第一位,重启一般就好了。
这里给一个实操建议:生产机器建议把启动顺序锁定为“硬盘优先”,并关闭不用的启动介质通道。更重要的是装系统前就要想好用传统 BIOS 还是 UEFI,千万别混用。如果磁盘分区表是 MBR,但固件强制走 UEFI 模式,GRUB 装不进去,启动必然失败。我帮同事处理“系统装好了重启找不到启动入口”的问题时,遇到过好几次这种基础交接错误,最后都耗费了大量时间。
2.3 Secure Boot 和启动模式的选择会影响安装成败
UEFI 时代多了个 Secure Boot,简单说就是固件只允许启动“被它信任的”引导程序。大部分主流发行版用 shim 引导层做签名转发,默认开启 Secure Boot 也能安装。但如果你用的是冷门发行版或自己编译的内核,开机时可能会被拦在固件阶段。
一个典型场景:在一台新笔记本上装基于某个 Linux 的定制系统,装完重启后屏幕直接提示签名校验失败,原因就是固件开了 Secure Boot,而引导程序没有可信签名。处理方式一般是进固件界面把 Secure Boot 关掉,或者把自定义引导程序加入信任列表。
要提醒的是:关闭 Secure Boot 会降低对引导层攻击的防护。在自己的机器上练手无所谓,但在办公机、生产环境就要权衡一下。第一棒到这里结束,交接信号是固件找到了启动设备,把控制权转交给磁盘上的引导程序。如果你卡在这一棒,屏幕通常停在厂商 Logo,或者出现“No bootable device”“Boot Device Not Found”一类的提示。
3. 第二棒:GRUB 如何在几秒内把内核“请”出来
3.1 一个自己会读文件系统的引导小程序
固件把接力棒交给 GRUB 之后,GRUB 需要在极短时间内完成两件事:给用户选择启动项的机会(如果你有多系统或多种内核版本),然后把内核镜像和配套的 initramfs 从磁盘读入内存。
听上去简单,但这里有个容易被忽视的技术点:磁盘上的根分区通常是 ext4、XFS、Btrfs 这类文件系统,甚至可能放在 LVM 或 RAID 上层。GRUB 自己实现了这些文件系统的读取逻辑,所以才能直接读出内核文件。换句话说,GRUB 本质上是一个迷你操作系统,它内置了大量文件系统驱动。
为什么要做这么重?因为内核在启动早期还不能识别复杂存储布局,必须由一个独立的小程序先把内核从复杂文件系统里捞出来。就好比你先得有个会读地图的人走到仓库,才能把专业司机请出来开卡车。如果 GRUB 读不到 /boot 下的内核镜像,启动就会停在这一棒,屏幕上可能出现 “error: file not found” 或者直接退回 GRUB rescue shell。
3.2 读懂 grub.cfg 里的关键配置
GRUB 的配置文件通常位于 /boot/grub2/grub.cfg(RHEL/CentOS/Rocky 系)或者 /boot/grub/grub.cfg(Debian/Ubuntu 系)。我建议新手去读一下这个文件的 menuentry 段落,你会看到里面写着 kernel 命令行、initrd 路径等关键内容。
一个典型的 menuentry 片段长这样:
menuentry 'Rocky Linux (6.10.0-rc1)' { load_video set gfxpayload=keep insmod gzio insmod part_gpt insmod ext2 search --no-floppy --fs-uuid --set=root e2b5f6c2-xxxx-xxxx-xxxx-xxxxxxxxxxxx linuxefi /vmlinuz-6.10.0-rc1 root=UUID=8a7b6c5d-xxxx-xxxx-xxxx-xxxxxxxxxxxx ro crashkernel=auto quiet initrdefi /initramfs-6.10.0-rc1.img }这里有几个点值得细看。search --fs-uuid是在告诉 GRUB:别死记盘符,要根据文件系统 UUID 去找根分区所在的设备。用 UUID 而不是 /dev/sda1 是 Linux 启动环节的基本习惯,因为内核初始化磁盘的顺序不一定和设备名对应,UUID 才是稳定标识。root=UUID=xxx是传给内核的参数,告诉内核真正的根分区在哪个设备上。这个参数如果写错,启动会直接卡在“无法打开根设备”的位置。
kernel 命令行里的ro表示先以只读方式挂载根文件系统,quiet是压制启动时的冗余输出,crashkernel=auto是给 kdump 预留内存。这些参数看起来平淡无奇,但在排查启动问题时,你可能要临时在 GRUB 菜单里修改它们——比如去掉quiet来看内核启动的完整输出,或者删掉某些参数来绕过一个异常驱动。平时用不到的技能,关键时刻很能救命。我自己排障经验里,有三分之一的问题是从临时编辑 GRUB 菜单开始的。
3.3 临时编辑 GRUB 启动参数是排障基本功
分享一个实操套路:在 GRUB 菜单界面按e进入编辑模式,找到以linux开头的那一行(注意是内核配置那行,不是 initrd 那行),到行尾手动去掉quiet,甚至可以加上single进入单用户模式,或者加上systemd.unit=rescue.target进入救援模式。改完按Ctrl+X或F10启动,这个改动只对这一次启动生效,不会写回配置文件。
这个技巧特别适合系统起不来、但你又想保留原始配置的场景。比如有一次客户的机器因为某个服务异常,反复进入卡死状态,我就是用这个方式把systemd.unit=rescue.target追加到内核参数后面,绕过出问题的服务,进入系统后再慢慢排查,修好后再正常重启。整个过程不用改 grub.cfg,也不会因为手误把引导配置改坏。
GRUB 这一棒跑完,接力棒就交到内核手上。交接瞬间,屏幕上通常会出现 GRUB 菜单倒计时结束画面,然后闪过一行行 “Loading Linux…” 和 “Loading initial ramdisk…”。如果你看到这里没有后续了,别急着怀疑后面的组件,先想想是不是内核镜像本身损坏、磁盘读取失败,或者内存不足导致内核解压失败。
4. 第三棒和第四棒:内核启动与 initramfs 的临时拼图
4.1 内核解压之后,第一批打印信息来自哪里
GRUB 加载到内存的 vmlinuz 是经过压缩的内核镜像。内核的第一件事是自解压,把自己展开到内存里,然后执行 start_kernel() 入口函数。从这里开始,内核才真正“活”过来,完成中断处理初始化、内存页表建立、调度器初始化、创建几个最早的内核线程在内的一系列动作。
这个过程对外部观察者来说几乎瞬时,但有个概念一定要建立:在这个阶段,内核是孤零零的,它还没有任何用户态程序可依赖,所有打印都直接输出到屏幕或串口。你在笔记本上按电源键后看到的满屏滚动日志,或者服务器通过 IPMI 看到的启动输出,很多都来自这个阶段。内核维护者专门提供了早期控制台机制,让启动早期打印可以被重定向到 VGA 屏或者串口,这也是内核调试和嵌入式开发时定位问题的重要入口。
4.2 initramfs:一间临时更衣室
内核初始化完自己之后,马上要面对一个现实问题:它要挂载真正的根文件系统,可这时候它手里的驱动还不齐全。很多磁盘控制器、NVMe 驱动、文件系统模块都还没加载,光凭内核内置驱动,很可能连根分区都认不出来。解决这个死局的关键,就是 initramfs,也就是早期用户空间。
initramfs 是一个压缩的临时文件系统镜像,在 /boot 目录下你可以看到 initramfs-xxx.img。GRUB 把它一起读入内存后,内核在内存里展开这个镜像,作为临时根文件系统,然后执行其中的 /init 脚本。这个脚本的作用是“搭脚手架”:加载磁盘驱动、组装 LVM 或 RAID 卷、解密 LUKS 加密分区、挂载真正的根文件系统,最后用 switch_root 把系统根目录从内存中的临时根切到磁盘上的真实根。
你可以这样理解 initramfs:它是一间临时更衣室。长跑运动员刚到场,还没换上正式跑鞋,更衣室里已经有人帮他准备好了装备,他换好装备再上赛道。对系统管理员来说,平时可能用不到这些细节,但你改了 fstab 里的 UUID、或者给磁盘开了 LUKS 加密却没有更新 initramfs 时,系统启动就会卡在这一棒。屏幕上可能出现 “Timed out waiting for device” 或者直接跳到紧急 shell,因为内核找不到它要的根设备。
4.3 switch_root 和根文件系统的交接
构建过 initramfs 或者写过早期启动脚本的人,一定见过 switch_root 和 pivot_root 这两个工具。它们的目标都是切换根文件系统,但侧重点不同。pivot_root 是相对旧的方案,适合把原根目录保留在某个挂载点上,用来构造 chroot 环境;switch_root 更彻底,它会直接切换根目录,然后执行新根下的 /sbin/init。现代 initramfs 几乎都走 switch_root 路线,因为它干净,不会在内存里残留没用的临时文件系统。
我在实际调试中遇到过一种情况:自己重新制作的 initramfs 里少复制了一个动态库文件,启动脚本执行到 switch_root 时直接报 “No such file or directory”,系统卡住。当时我第一反应是怀疑内核驱动问题,后来用 dmesg 一查,错误信息明确指向用户态工具缺失。这件事给我的教训是:不要在排障时只盯着“启动过程”这个抽象概念,它每一棒都有自己独立的错误日志和排查方式,先把报错内容看仔细,再决定去查哪一层。
到这里,接力棒已经从内核交到用户空间。交接标志是屏幕上出现类似 “Run /init as init process” 或 “Switch root to real root filesystem” 的日志,紧接着系统开始执行 /sbin/init。在主流发行版里,它就是 systemd。从这一刻起,你已经从内核态进入用户态,之后的启动过程相比前面会慢得多,因为等待你的是一大堆服务的启动和依赖解析。
5. 第五棒:systemd 成为 PID 1,用户态世界的总调度
5.1 PID 1 为什么这么特殊
内核完成根文件系统切换后,会启动一个 PID 为 1 的进程。在绝大多数现代发行版中,这个进程就是 systemd。PID 1 的特殊之处在于,它几乎是所有其他进程的祖先。普通进程如果变成孤儿进程,会被内核重新挂在 PID 1 名下,由它接管。你可以用ps -p 1查看:
[root@localhost ~]# ps -p 1 -o pid,comm,args PID COMMAND COMMAND 1 systemd /usr/lib/systemd/systemd --switched-root --system --deserialize=25注意命令行里的--switched-root,它表示 systemd 是在 switch_root 完成之后被执行的,正好对应上一棒的内容。--deserialize=25是内核和 systemd 之间传递早期状态的文件描述符,普通运维不需要深究,但看到它不会慌,知道这是正常状态。
systemd 要做的第一件事是加载单元文件、建立依赖关系、启动挂载和交换分区。它和传统 SysV init 最大的区别是并行化:传统 init 按脚本顺序一个接一个跑,systemd 在保证依赖的前提下,尽可能同时启动互不依赖的服务。打个比方,传统 init 像一个人按清单逐项买菜,systemd 则像指挥一支小队分头行动:有人拿酱油,有人买鸡蛋,最后在厨房汇合。
5.2 unit、target 和依赖关系的理解方法
systemd 把要管理的对象都抽象成 unit,比如 service、mount、socket、device 等。每个 unit 文件里的 [Unit] 段包含 Requires、Wants、After 字段,用来控制启动依赖和行为。给新手一个记忆方法:Wants 表示“我希望它在”,但失败不影响我继续;Requires 表示“我必须要它”,关联失败我自己也跑不了;After 只决定启动顺序,不决定依赖关系。这三个字段是排查“为什么某个服务起不来”的高频切入点。
比如你写了一个服务 A,要求网络可用后才启动,就在 A 的服务文件里写:
[Unit] Description=My custom service After=network-online.target Wants=network-online.target这样即使网络服务启动失败,A 也只是打印警告,而不是直接拒绝启动。类似细节很多系统学习 systemd 的文档里虽有提到,但实际部署时特别容易用错。
target 则是“启动阶段”的概念。systemd 将启动过程拆分成一层层 target,类似传统 init 的运行级别。比如 poweroff.target、rescue.target、multi-user.target、graphical.target。默认开机目标一般由 /etc/systemd/system/default.target 指向。你也可以在启动时用systemd.unit=rescue.target这个内核参数,强制系统只启动到救援模式,这在排障时非常有用,前面讲 GRUB 临时编辑参数时已经提过一次。
5.3 从内核态到用户态:systemd 实际在启动什么
在默认的 multi-user.target 下,systemd 会陆续启动网络管理、磁盘挂载、时间同步、日志服务、cron、ssh 等一系列基础服务。发行版不同,服务清单差异很大,但有一个原则是共通的:能并行就并行,能延迟就延迟,能用 socket 激活的就别急着起进程。
socket 激活这个概念值得一提。像某些打印服务、日志收集、甚至数据库,并不一定开机就把进程常驻,而是启动一个监听 socket,真正有客户端连接时才拉起进程。这对系统启动时间很友好,也减少了不必要的常驻内存消耗。很多新手在进程列表里没看到某个服务,就以为它没启动,其实它可能是被 systemd 用 socket 激活机制“按需拉起”了。这个机制在分析“为什么进程列表没有某个服务但端口是通的”时特别重要。
到这里,系统已经把所有核心服务拉起来了。屏幕上的表现是:传统服务器弹出多个 getty 登录页面,桌面发行版启动 gdm、sddm 等显示管理器。第五棒交接完成,接下来到终点线——你看到登录提示符的那一刻。
6. 终点线:登录提示符是如何出现在你面前的
6.1 getty 与命令行登录的完整链条
服务器进入 multi-user.target 后,systemd 会启动多个 getty 实例,每个 getty 对应一个 tty 设备,比如 tty1、tty2。getty 的作用是初始化终端线路,在屏幕上打印登录提示,等待用户输入用户名。输入用户名并回车后,getty 把控制权交给 login 程序,login 校验密码、读取用户 shell 配置,最终执行用户的默认 shell,比如 /bin/bash。你在终端里看到的 user@host 提示符,实际上经过了一连串程序的搬运。
想看当前系统开了哪些 getty 实例,可以执行:
systemctl status 'getty@*'我常接到一种求助电话:按了 Ctrl+Alt+F2,切到一个陌生的 tty 界面,键盘没反应。这种情况十有八九是那个 tty 上没有 getty 在运行,或者服务被误停了。重启 getty 的方法很简单:
systemctl restart getty@tty2.service6.2 图形登录:显示管理器把最后一棒跑完
桌面版 Linux 的终点线不是 getty,而是显示管理器。它先启动图形服务器(Xorg 或 Wayland compositor),再弹出登录窗口。如果登录成功,显示管理器负责拉起完整的桌面会话,比如 GNOME、KDE 或 Xfce。
这里有一个容易踩的坑:很多时候图形界面卡住,问题不在桌面环境,而在显示管理器。显示管理器是 systemd 启动的服务,日志写进 journald;桌面环境一旦起来了,就是普通用户进程,日志可能只存在用户自己的 journal 里。区分这两层,能帮你节约大量排障时间。比如有一次我遇到黑屏加鼠标光标能动的桌面问题,先查的是 GDM 服务的日志,发现是显卡驱动没加载成功,于是去查内核模块。如果我一上来就折腾桌面配置,大概率白忙。
6.3 回看整个链路,启动问题究竟出在哪一层
现在从终点回看整个启动过程,应该能明白:Linux 启动的本质不是某个程序“加载系统”,而是多个独立模块按固定接口依次交接。固件负责硬件,GRUB 负责引导,内核负责抽象硬件和文件系统,initramfs 负责搭桥,systemd 负责组织用户态服务,最后是登录管理。每一棒都有自己的启动日志、配置文件和排障工具。
这是运维新手容易犯的认知错误:启动一卡住,就去看系统日志、怀疑某个服务,但实际上卡住的阶段可能早于日志服务产生任何记录。比如 systemd-journald 还没跑起来,你当然查不到服务日志;内核早期阶段的问题,要靠 dmesg 或串口输出来确认。掌握启动过程的真正价值,不是背知识点,而是能快速判断“这是第几棒的问题”。
7. 启动卡住时,如何快速定位是哪一棒掉了链子
7.1 先看屏幕现象,再决定查什么
启动排障的第一步永远是观察现象,而不是急着敲命令。我做技术支援时,开口第一句话通常是:“现在屏幕停留在什么画面?”根据画面就能缩小问题域。
| 屏幕现象 | 可能出问题的阶段 | 优先排查方向 |
|---|---|---|
| 卡在厂商 Logo,无启动文字 | BIOS/UEFI 阶段 | 固件设置、启动顺序、硬件自检 |
| 卡在 GRUB 菜单倒计时后黑屏 | GRUB 加载内核阶段 | 内核镜像完整性、GRUB 配置、Secure Boot |
| 有滚屏日志但停在某一行 | 内核早期初始化 | dmesg 输出、内核参数、initramfs |
| 卡在 “A start job is running for…” | systemd 服务启动 | systemd-analyze、journalctl |
| 出现紧急 shell 提示符 | initramfs 或根文件系统挂载失败 | 根分区 UUID、fstab、磁盘驱动 |
这张表来自我的实际排障经验,准确率很高。核心思路就是先根据界面判断是第几棒,再拿对应阶段的日志工具去查,别在一个地方死磕。
7.2 systemd-analyze 是启动体检的利器
如果系统能正常启动,只是慢,或者你怀疑某阶段耗时过长,systemd-analyze 是首选工具。它有三种最常用的用法。
systemd-analyze time:显示固件、内核、用户态各阶段的总耗时。systemd-analyze blame:按耗时从高到低列出所有服务的启动时间,一眼看出哪个服务拖后腿。systemd-analyze critical-chain:显示某个目标或服务的启动链路依赖顺序和各环节耗时。
举个例子,执行 blame 后看到某个 cloud-init 服务占了 23 秒,而你根本不用云环境,直接禁用即可,比手动在服务列表里猜效率高得多。
需要注意的是,blame 统计的是“从触发到 ready”的时间,并不等于进程自身占用 CPU 的时间。有些服务是因为等待网络或依赖而慢,不代表它本身不健康。所以排查时还要结合journalctl -b看本次启动的完整日志。
7.3 journalctl -b 与 dmesg 的分工怎么用
journalctl -b查看的是本次启动以来 systemd 和用户态服务的日志,适合排查 systemd 阶段的问题。dmesg查看内核环形缓冲区里保留的信息,适合排查内核早期初始化阶段的问题。两者分工非常清晰。
我的习惯是这样:先用 dmesg 确认内核有没有报明显的硬件错误,再用 systemd-analyze critical-chain 找到最慢的服务,最后用journalctl -b -u 服务名看具体某个服务为什么起不来。三步走下来,大部分启动问题都能定位到具体组件。
还有一个小技巧:journalctl -b -1可以查看上一次启动的日志。这个参数在“系统重启后问题消失、但还想分析当时发生了什么”时特别好用。比如客户报障说昨天机器重启后出问题,今天已经正常了,你就可以用-b -1抓取上一次启动的记录,不用错过现场。
7.4 几个常见的“假启动故障”与排查直觉
分享几个真实场景,帮新手建立排查直觉。
第一个是“启动卡在 A start job is running for dev-disk-by-x”。这个提示的意思是 systemd 在等待某个磁盘设备出现,但它一直没出现。常见原因包括 fstab 里写了不存在的 UUID、根分区对应的磁盘没接上、或者磁盘上做了软 RAID 但阵列没激活。破解方法是进紧急模式,检查 fstab 和 lsblk 输出,把不存在的挂载项修正一下。
第二个是“启动后网络不可用,但服务看起来都正常”。这种情况通常不是启动过程的问题,而是网络服务与 NetworkManager 或 systemd-networkd 之间的配置冲突。先区分用的是哪个网络管理工具,再看对应配置文件,别盲目重启网络服务。
第三个是“黑屏但系统启动其实成功了”。我在一台只有核显的机器上遇到过,最后发现是内核参数里没有合适的视频输出模式。临时在 GRUB 里删掉 quiet,或者加上 nomodeset,就能看到启动输出。nomodeset 对很多显卡驱动有特殊意义,排障时可以先试试绕开 KMS 模块的问题。
8. 实测过的启动优化:把每棒时间压下来但别丢稳定性
8.1 先测量再动刀,否则优化就是玄学
优化启动时间之前一定要先测量。我见过有人一上来就禁服务、删内核模块,最后系统倒是起得快了,功能也没了。正确流程是:用 systemd-analyze time 看哪一阶段耗时最长,用 blame 找出具体服务,在决定要不要动它。
比如一台老服务器,固件阶段固定要 8 秒,systemd 阶段要 30 秒。你花大力气优化 systemd,最多也就节省几秒;但如果在固件里开启 fast boot、关闭不必要的硬件检测,省下的时间可能更可观。投资回报率不一样,时间要花在刀刃上。
8.2 systemd 层面的常用优化手段
第一,禁用不需要的服务。用systemctl disable关闭 cloud-init、邮件服务、打印服务等开机项目。注意 disable 不是 stop,它只取消开机启动,不会立即影响当前运行状态。
第二,延迟非关键服务。有些服务不急开机启动,可以改成按需启动。比如容器运行时、本地数据库,如果只在特定任务时才用,可以改用 socket 激活或手动启动。
第三,精简 initramfs。用 dracut 重新生成 initramfs,只包含当前机器的必要驱动。命令示例:
dracut --hostonly --omit-drivers "floppy" /boot/initramfs-$(uname -r).img $(uname -r)这个命令会根据当前机器的实际硬件精简驱动,生成的 initramfs 通常比发行版默认的小很多,加载和解压更快。不过注意,如果这台机器的硬件会变化,比如需要外接 USB 磁盘启动,--hostonly 反而会引入风险,生产环境要评估清楚再动。
8.3 内核参数与稳定性的取舍
在 GRUB 的 kernel 命令行里加fastboot参数,可以抑制一些启动阶段的检查等待;加quiet只减少输出,不改变执行顺序。如果你想看清启动过程,建议去掉 quiet 而不是加 fastboot,因为前者更直观、可控性更强。
最后我想强调一件事:优化启动时间的前提,是别让稳定性为速度让步。特别是生产服务器,与其追零点几秒的开机时间,不如把精力放在可靠的挂载顺序、严谨的服务依赖和清晰的日志输出上。启动过程是接力赛,优化是在保证交接不出错的前提下缩短每棒的时间,而不是把接力棒直接扔过终点线。