概述

Linux 系统的启动是一个精密编排的多阶段过程,从固件加电自检到内核加载、再到用户空间服务启动,每个阶段都有其特定职责。理解完整的启动流程不仅有助于排查启动故障,还能进行启动性能优化。从固件层出发,逐层解析 BIOS/UEFI、GRUB2、initramfs、内核初始化、systemd 启动流程,并覆盖启动优化和内核崩溃恢复。

启动流程概览

[电源开启]
[1. 固件阶段] BIOS / UEFI
    │  POST (加电自检)
    │  硬件初始化
    │  查找启动设备
[2. 引导阶段] GRUB2
    │  加载 GRUB 到内存
    │  读取 grub.cfg
    │  加载内核和 initramfs
[3. 内核阶段] Linux Kernel
    │  内核解压并初始化
    │  硬件检测和驱动加载
    │  挂载 initramfs
    │  启动 init (systemd)
[4. 用户空间阶段] systemd
    │  读取 default.target
    │  按依赖顺序启动服务
    │  启动登录服务
[5. 登录阶段]
    │  getty / display-manager
    │  用户认证
    │  Shell 启动
[系统就绪]

BIOS 与 UEFI

BIOS vs UEFI

特性BIOSUEFI
启动模式LegacyUEFI
固件接口16位32/64位
分区表MBR (≤2TB)GPT (>2TB)
启动代码512字节MBREFI分区
启动速度
网络功能有(PXE/HTTP启动)
安全启动不支持支持
分辨率文本模式图形模式

BIOS 启动流程

1. POST (Power-On Self Test)
   ├── CPU 初始化
   ├── 内存检测
   ├── 硬件检测
   └── BIOS 设置检查

2. 启动设备选择
   ├── 按 BIOS 设置的顺序
   ├── 第一个可启动设备
   └── 读取 MBR (前 512 字节)

3. MBR 结构
   ├── 446 字节: 引导代码
   ├── 64 字节: 分区表 (4个主分区)
   └── 2 字节: 魔数 (0x55AA)
   
4. 执行 MBR 中的引导代码
   └── 加载 GRUB 的 core.img

UEFI 启动流程

1. POST
   └── 同 BIOS

2. UEFI 固件初始化
   ├── 初始化 UEFI 驱动
   ├── 运行 UEFI 应用
   └── 安全启动验证

3. 启动管理器
   ├── 读取 BootOrder 变量
   ├── 查找 EFI System Partition (ESP)
   │   └── 通常在 /dev/sda1 (FAT32, ~512MB)
   └── 执行 EFI 应用: /EFI/<distro>/grubx64.efi

4. 安全启动 (Secure Boot)
   ├── 验证 EFI 应用签名
   ├── 使用内核内置密钥
   └── 拒绝未签名或签名无效的引导程序

UEFI 启动项管理

# 查看 UEFI 启动项
$ efibootmgr
BootCurrent: 0000
BootOrder: 0000,0001,0002
Boot0000* ubuntu    HD(1,GPT,...)/File(\EFI\ubuntu\grubx64.efi)
Boot0001* UEFI:USB   ...
Boot0002* UEFI:CDROM ...

# 添加启动项
$ efibootmgr -c -d /dev/sda -p 1 -L "My Linux" -l '\EFI\mylinux\grubx64.efi'

# 修改启动顺序
$ efibootmgr -o 0000,0001,0002

# 删除启动项
$ efibootmgr -b 0002 -B

安全启动

# 查看安全启动状态
$ mokutil --sb-state
SecureBoot enabled

# 查看已注册的密钥
$ mokutil --list-enrolled

# 注册新的密钥(需要重启进入 MokManager)
$ mokutil --import /path/to/key.der

# 禁用安全启动(需要在 UEFI 设置中操作)
# 重启 → 进入 UEFI 设置 → Security → Secure Boot → Disabled

GRUB2 配置

GRUB2 启动阶段

1. boot.img (446字节)
   ├── 位于 MBR 或 ESP
   └── 加载 core.img

2. core.img (32KB)
   ├── diskboot.img: 从磁盘加载
   ├── lzma_decompress.img: 解压
   └── kernel.img: GRUB 核心

3. GRUB 正常运行
   ├── 读取 /boot/grub/grub.cfg
   ├── 显示启动菜单
   └── 加载选定的内核

GRUB 配置文件层级

/etc/default/grub          ← 主配置文件(用户修改)
/etc/grub.d/               ← 脚本目录
  ├── 00_header             ← 头部配置
  ├── 10_linux              ← Linux 菜单项
  ├── 20_memtest            ← Memtest 菜单项
  ├── 30_os-prober          ← 其他 OS 检测
  └── 40_custom             ← 自定义菜单项
/boot/grub/grub.cfg        ← 生成的配置(不要手动修改)

主配置文件

# /etc/default/grub

# 启动菜单等待时间(秒)
GRUB_TIMEOUT=5

# 默认启动项(0 = 第一项)
GRUB_DEFAULT=0

# 菜单显示方式
GRUB_TIMEOUT_STYLE=menu    # menu/countdown/hidden

# 内核参数
GRUB_CMDLINE_LINUX="quiet splash"
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"

# 禁用子菜单(显示所有内核)
GRUB_DISABLE_SUBMENU=y

# 禁用恢复模式
GRUB_DISABLE_RECOVERY=true

# 分区 UUID
GRUB_DISABLE_LINUX_UUID=false

# 图形终端
GRUB_TERMINAL=gfxterm
GRUB_GFXMODE=1920x1080

# 背景图片
GRUB_BACKGROUND="/boot/grub/background.png"

# 字体
GRUB_FONT="/boot/grub/fonts/unicode.pf2"

# 串口控制台(服务器场景)
#GRUB_TERMINAL="serial console"
#GRUB_SERIAL_COMMAND="serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1"

内核参数详解

# /etc/default/grub
GRUB_CMDLINE_LINUX=""

# 常用内核参数:
# quiet         - 隐藏启动消息
# splash        - 显示启动画面
# nomodeset     - 不加载图形驱动(排查显示问题)
# text          - 启动到文本模式
# single / 1    - 单用户模式
# rescue        - 救援模式
# init=/bin/bash - 替换 init(紧急恢复)
# systemd.unit=rescue.target - 启动到救援模式
# systemd.unit=emergency.target - 启动到紧急模式

# 调试参数:
# debug         - 启用内核调试
# loglevel=8    - 内核日志级别
# print-fatal-signals=1 - 打印信号信息

# 性能参数:
# nohz_full=1-7 - 无 tick CPU
# isolcpus=1-7  - 隔离 CPU
# transparent_hugepage=never - 禁用 THP

# 安全参数:
# apparmor=1    - 启用 AppArmor
# audit=1       - 启用审计

生成 GRUB 配置

# Debian/Ubuntu
$ update-grub
# 或
$ grub-mkconfig -o /boot/grub/grub.cfg

# RHEL/CentOS
$ grub2-mkconfig -o /boot/grub2/grub.cfg

# UEFI 系统 (RHEL/CentOS)
$ grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg

自定义启动项

# /etc/grub.d/40_custom
#!/bin/sh
exec tail -n +3 $0

menuentry "Custom Linux Rescue" {
    set root='(hd0,msdos1)'
    linux /boot/vmlinuz-rescue root=/dev/sda1 ro single
    initrd /boot/initramfs-rescue.img
}

menuentry "Windows 10" {
    insmod part_gpt
    insmod chain
    set root='(hd0,gpt1)'
    chainloader /EFI/Microsoft/Boot/bootmgfw.efi
}

menuentry "Memtest86+" {
    linux16 /boot/memtest86+.bin
}

GRUB 密码保护

# 1. 生成密码哈希
$ grub-mkpasswd-pbkdf2
Enter password: 
Reenter password: 
PBKDF2 hash of your password is grub.pbkdf2.sha512.10000.ABC123...

# 2. 添加到 /etc/grub.d/40_custom
set superusers="admin"
password_pbkdf2 admin grub.pbkdf2.sha512.10000.ABC123...

# 3. 为特定菜单项添加保护
# 在 /etc/grub.d/10_linux 的 menuentry 中添加 --users ""
# 允许无需密码启动默认项,但编辑需要密码

# 4. 重新生成配置
$ update-grub

GRUB 命令行模式

# 在 GRUB 菜单界面按 'c' 进入命令行

# 查看可用命令
grub> help

# 查看磁盘
grub> ls
(hd0) (hd0,msdos1) (hd0,msdos2)

# 查看分区内容
grub> ls (hd0,msdos1)/
boot/  etc/  home/  ...

# 手动启动
grub> set root=(hd0,msdos1)
grub> linux /boot/vmlinuz-6.6.0 root=/dev/sda1 ro
grub> initrd /boot/initramfs-6.6.0.img
grub> boot

# 搜索文件
grub> search.file /boot/grub/grub.cfg

initramfs

initramfs 的作用

initramfs(Initial RAM Filesystem)是一个临时的根文件系统,在内核启动后、真正的根文件系统挂载前使用。其主要职责:

  1. 加载必要的内核模块(存储驱动、文件系统驱动)
  2. 查找并挂载真正的根文件系统
  3. 切换到真正的根文件系统
内核启动 → 挂载 initramfs → 加载驱动 → 挂载真实根 → switch_root → systemd

initramfs 结构

# 查看 initramfs 内容
$ lsinitramfs /boot/initrd.img-$(uname -r) | head -30
.
bin
bin/busybox
conf
conf/conf.d
conf/init
cryptroot
etc
etc/ld.so.conf
etc/ld.so.conf.d
...
init
lib
lib/modules/6.6.0/kernel/drivers/
...
scripts
scripts/init-premount
scripts/local-top

initramfs 的 init 脚本

# initramfs 的 /init 脚本(简化版)
#!/bin/sh

# 1. 挂载基本文件系统
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev

# 2. 加载内核模块
modprobe ext4
modprobe sd_mod
modprobe nvme

# 3. 解析内核命令行参数
# root=/dev/sda1
# 或 root=UUID=xxx
# 或 root=LABEL=root

# 4. 查找并挂载根文件系统
mount /dev/sda1 /mnt/root

# 5. 切换到真实根文件系统
exec switch_root /mnt/root /sbin/init

重新生成 initramfs

# Debian/Ubuntu
$ update-initramfs -u            # 更新当前内核
$ update-initramfs -u -k all     # 更新所有内核
$ update-initramfs -c -k 6.6.0   # 为指定内核创建

# RHEL/CentOS
$ dracut -f                      # 强制重新生成
$ dracut -f /boot/initramfs-6.6.0.img 6.6.0

# 查看包含的模块
$ lsinitramfs /boot/initrd.img-$(uname -r) | grep "kernel/drivers/"
# 或
$ dracut --list-modules

添加模块到 initramfs

# Debian/Ubuntu: /etc/initramfs-tools/modules
# 添加需要的模块名
nvme
nvme-core
ext4
xfs

# 重新生成
$ update-initramfs -u

# RHEL/CentOS: /etc/dracut.conf.d/custom.conf
add_drivers+=" nvme nvme-core ext4 xfs "
force_drivers+=" my_custom_driver "

$ dracut -f

initramfs 故障排查

# 1. 在 GRUB 中添加 debug 参数
# GRUB_CMDLINE_LINUX="rd.debug"

# 2. 添加 shell 到 initramfs
# GRUB_CMDLINE_LINUX="rd.break"
# 在 initramfs 阶段暂停,进入 shell

# 3. 常见问题
# - 根文件系统找不到: 缺少存储驱动
# - LVM/LUKS 无法解锁: 缺少 lvm2/cryptsetup
# - NFS 根挂载失败: 缺少 nfs 模块

内核初始化

内核启动过程

1. 内核解压
   ├── 从 bzImage 解压到内存
   └── 跳转到内核入口

2. 早期初始化
   ├── CPU 初始化
   ├── 内存子系统初始化
   ├── 页表设置
   └── 中断控制器初始化

3. 硬件检测
   ├── PCI 总线扫描
   ├── 设备树解析(ARM)
   ├── ACPI 表解析(x86)
   └── 加载内置驱动

4. 内存管理初始化
   ├── buddy 分配器
   ├── slab 分配器
   └── vmalloc 区域

5. 调度器初始化
   ├── 创建 init 任务 (PID 0)
   └── 启动 idle 进程

6. 挂载 initramfs
   ├── 解压 cpio 归档
   └── 执行 /init

7. 挂载真实根文件系统
   └── switch_root 到 /sbin/init

内核启动参数

# 在 GRUB 中传递内核参数
# /etc/default/grub
GRUB_CMDLINE_LINUX="参数列表"

# 临时在 GRUB 菜单中修改
# 按 'e' 编辑 → 在 linux 行末尾添加参数 → Ctrl+X 启动

# 查看当前启动参数
$ cat /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz-6.6.0 root=UUID=xxx ro quiet splash

# 常用故障排查参数:
# single          - 单用户模式
# rescue          - 救援模式
# emergency       - 紧急模式
# init=/bin/bash  - 直接启动 bash(跳过 systemd)
# systemd.unit=rescue.target
# rd.break        - 在 initramfs 阶段暂停
# root=/dev/sda1  - 指定根分区
# rootflags=subvol=root - 根分区挂载选项
# nomodeset       - 不加载图形驱动
# acpi=off        - 禁用 ACPI
# noapic          - 禁用 APIC
# nodmraid        - 禁用 dmraid
# rd.shell        - 提供紧急 shell

内核启动日志

# 查看本次启动的内核日志
$ dmesg
# 或
$ journalctl -k

# 查看上次启动的内核日志
$ journalctl -k -b -1

# 查看启动耗时
$ systemd-analyze
Startup finished in 5.123s (kernel) + 12.456s (userspace) = 17.579s

# 查看各服务启动耗时
$ systemd-analyze blame | head -20
12.345s systemd-journal-flush.service
 8.123s networkd-dispatcher.service
 5.456s docker.service
 3.789s sshd.service
 ...

systemd 启动流程

systemd 初始化阶段

1. 早期初始化
   ├── 读取 /etc/systemd/system.conf
   ├── 初始化核心功能
   ├── 挂载 /proc /sys /dev
   └── 启动基础服务

2. 加载默认 target
   ├── 读取 default.target
   ├── 通常为 graphical.target 或 multi-user.target
   └── 解析依赖关系

3. 按依赖启动
   ├── 拉起所有 Before= 的单元
   ├── 并行启动无依赖的服务
   └── 处理 Requires=/Wants= 关系

4. 启动完成
   ├── 所有 target 依赖满足
   ├── 启动登录服务
   └── 系统就绪

Target 与 Runlevel

RunlevelTarget说明
0poweroff.target关机
1rescue.target单用户模式
2multi-user.target多用户(无图形)
3multi-user.target多用户(无图形)
4multi-user.target自定义
5graphical.target多用户+图形
6reboot.target重启
# 查看当前 target
$ systemctl get-default
multi-user.target

# 查看所有 target
$ systemctl list-units --type=target

# 修改默认 target
$ systemctl set-default graphical.target

# 临时切换 target
$ systemctl isolate rescue.target
$ systemctl isolate multi-user.target

Target 依赖关系

graphical.target
    ├── Wants: multi-user.target
    └── multi-user.target
            ├── Wants: basic.target
            └── basic.target
                    ├── Wants: sysinit.target
                    └── sysinit.target
                            ├── systemd-update-utmp.service
                            ├── systemd-tmpfiles-setup.service
                            └── ...

启动阶段关键服务

1. sysinit.target 阶段(最早)
   ├── systemd-journald.service    # 日志服务
   ├── systemd-modules-load.service # 加载内核模块
   ├── systemd-tmpfiles-setup-dev.service # /dev
   └── kmod-static-nodes.service

2. basic.target 阶段(基础就绪)
   ├── paths.target
   ├── slices.target
   ├── sockets.target
   ├── timers.target
   └── systemd-udevd.service

3. multi-user.target 阶段(多用户就绪)
   ├── sshd.service
   ├── network.service / NetworkManager.service
   ├── cron.service
   ├── getty.target                # 登录终端
   └── docker.service

4. graphical.target 阶段(图形就绪)
   ├── display-manager.service     # GDM/LightDM
   └── multi-user.target

服务依赖管理

# 查看服务依赖关系
$ systemctl list-dependencies nginx.service

# 查看反向依赖(谁依赖此服务)
$ systemctl list-dependencies --reverse nginx.service

# 查看服务的启动顺序
$ systemctl list-dependencies --after nginx.service
$ systemctl list-dependencies --before nginx.service

unit 文件中的依赖

# /etc/systemd/system/myservice.service
[Unit]
Description=My Service
Requires=network.target       # 强依赖(必须启动)
Wants=remote-fs.target         # 弱依赖(尽量启动)
After=network.target           # 在 network 后启动
Before=multi-user.target       # 在 multi-user 前启动
Conflicts=sendmail.service     # 互斥服务

[Service]
Type=notify
ExecStart=/usr/bin/myservice
Restart=on-failure

[Install]
WantedBy=multi-user.target
依赖类型说明失败行为
Requires=强依赖依赖失败则本服务也失败
Wants=弱依赖依赖失败不影响本服务
Requisite=强依赖(不启动)依赖未运行则本服务直接失败
Conflicts=互斥不能同时运行
After=顺序在指定 unit 后启动
Before=顺序在指定 unit 前启动

启动性能优化

分析启动耗时

# 总体耗时
$ systemd-analyze

# 各服务耗时
$ systemd-analyze blame | head -20

# 关键路径分析
$ systemd-analyze critical-chain
# 显示启动的关键路径上的服务

# 特定服务的关键路径
$ systemd-analyze critical-chain docker.service

# 生成启动图
$ systemd-analyze plot > boot-analysis.svg
# 在浏览器中打开 SVG 文件查看详细时间线

# 分析各阶段耗时
$ systemd-analyze --profile

常见启动慢的原因

原因现象解决方案
网络等待network.service 耗时长配置静态 IP 或减小 DHCP 超时
磁盘检测fsck 耗时定期执行 fsck,启用日志文件系统
硬件初始化udev 耗时排除不必要的硬件
服务串行服务未并行启动检查 After= 依赖是否过度
DNS 解析某服务等待 DNS配置本地 DNS 缓存
Entropy 不足某服务等待随机数安装 haveged 或 rng-tools

优化措施

1. 禁用不必要的服务

# 查看所有启用的服务
$ systemctl list-unit-files --state=enabled

# 禁用不必要的服务
$ systemctl disable bluetooth.service
$ systemctl disable cups.service
$ systemctl disable modemmanager.service
$ systemctl disable avahi-daemon.service
$ systemctl disable speech-dispatcher.service

2. 优化网络等待

# DHCP 超时优化
# /etc/dhcp/dhclient.conf
timeout 10;
retry 3;

# 或使用 NetworkManager
# /etc/NetworkManager/conf.d/dhcp.conf
[connection]
ipv4.dhcp-timeout=10

3. 减少内核模块加载

# 查看加载的模块
$ lsmod | wc -l

# 禁用不需要的模块
# /etc/modprobe.d/blacklist.conf
blacklist bluetooth
blacklist firewire-core
blacklist thunderbolt
blacklist uas

4. 优化 fsck

# 使用日志文件系统(ext4/xfs)减少 fsck 时间
# 调整 fsck 频率
# /etc/fstab
# 第 6 列: pass(0=不检查, 1=根分区优先, 2=其他分区)
/dev/sda1  /  ext4  defaults  0  1
/dev/sda2  /home  ext4  defaults  0  2

5. 并行化服务启动

# 检查服务是否设置了不必要的 After=
$ systemctl cat docker.service | grep -E "After=|Requires="

# 移除不必要的串行依赖
# 创建 override
$ systemctl edit docker.service
[Unit]
After=          # 清空默认的 After
After=network.target  # 只保留必要的

6. 使用 systemd-networkd 替代 NetworkManager

# systemd-networkd 启动更快
$ systemctl disable NetworkManager
$ systemctl enable systemd-networkd
$ systemctl enable systemd-resolved

# 配置
# /etc/systemd/network/20-wired.network
[Match]
Name=eth0

[Network]
DHCP=yes
# 或静态 IP
# Address=192.168.1.100/24
# Gateway=192.168.1.1
# DNS=8.8.8.8

优化效果验证

# 优化前
$ systemd-analyze
Startup finished in 5.1s (kernel) + 25.3s (userspace) = 30.4s

# 优化后
$ systemd-analyze
Startup finished in 4.8s (kernel) + 8.2s (userspace) = 13.0s

# 持续监控
$ systemd-analyze plot > before-optimization.svg
# 优化后
$ systemd-analyze plot > after-optimization.svg

内核崩溃恢复

内核 panic

# 内核 panic 时的行为
$ sysctl kernel.panic
kernel.panic = 0
# 0 = 不自动重启(挂起)
# N = N 秒后自动重启

# 推荐:生产环境设置 10 秒后重启
$ sysctl -w kernel.panic=10

kdump:内核崩溃转储

kdump 在内核崩溃时启动第二个内核(capture kernel),转储崩溃内核的内存。

# 1. 安装
$ apt install kdump-tools        # Debian/Ubuntu
$ dnf install kexec-tools        # RHEL/CentOS

# 2. 配置
# /etc/default/kdump-tools
KDUMP_SYSCTL="kernel.panic=10"
KDUMP_COREDIR="/var/crash"

# RHEL/CentOS: /etc/default/kexec
KDUMP_KERNEL="/boot/vmlinuz-$(uname -r)kdump"
KDUMP_INITRD="/boot/initramfs-$(uname -r)kdump.img"

# 3. 启用
$ systemctl enable kdump
$ systemctl start kdump

# 4. 验证
$ kdump-config show
# 或
$ kexec -p --status

# 5. 测试(触发 panic)
# ⚠️ 警告:这会导致系统崩溃!
$ echo 1 > /proc/sys/kernel/sysrq
$ echo c > /proc/sysrq-trigger

kdump 转储分析

# 查看崩溃转储
$ ls /var/crash/
202607101530/   # 按时间组织的目录

# 分析转储文件
$ crash /usr/lib/debug/boot/vmlinux-$(uname -r) /var/crash/202607101530/dump.202607101530

# crash 常用命令
crash> bt         # 查看崩溃时的调用栈
crash> ps         # 查看进程列表
crash> log        # 查看内核日志
crash> sys        # 系统信息
crash> vm <pid>   # 查看进程内存
crash> dev        # 查看设备信息

紧急恢复模式

rescue target(救援模式)

# 在 GRUB 中添加
systemd.unit=rescue.target

# 或在启动后
$ systemctl rescue

# rescue 模式:
# - 挂载根文件系统(只读)
# - 启动最小服务集
# - 需要 root 密码
# - 可以编辑文件

emergency target(紧急模式)

# 在 GRUB 中添加
systemd.unit=emergency.target

# 或
$ systemctl emergency

# emergency 模式:
# - 仅挂载根文件系统(只读)
# - 不启动任何服务
# - 需要 root 密码
# - 最小化环境

init=/bin/bash

# 在 GRUB 中添加
init=/bin/bash

# 或
linux /boot/vmlinuz-6.6.0 root=/dev/sda1 ro init=/bin/bash

# 效果:
# - 跳过 systemd
# - 直接进入 bash
# - 根文件系统只读
# - 无网络、无服务

# 重新挂载根文件系统为读写
mount -o remount,rw /

# 修改密码
passwd root

# 修改配置文件
vi /etc/fstab

# 重启
exec /sbin/init
# 或
mount -o remount,ro /
reboot -f

rd.break(initramfs 阶段暂停)

# 在 GRUB 中添加
rd.break

# 效果:
# - 在 initramfs 阶段暂停
# - 根文件系统未挂载
# - 可以修复 GRUB、initramfs 等

# 挂载真实根
switch_root /sysroot

# 修改 root 密码
mount -o remount,rw /sysroot
chroot /sysroot
passwd root
touch /.autorelabel    # SELinux 重新标记
exit
exit

SysRq 魔术键

# 启用 SysRq
$ echo 1 > /proc/sys/kernel/sysrq

# 常用 SysRq 组合(Alt+SysRq+字母)
# Alt+SysRq+s  - 同步文件系统
# Alt+SysRq+u  - 重新挂载为只读
# Alt+SysRq+b  - 立即重启
# Alt+SysRq+e  - 终止所有进程
# Alt+SysRq+i  - 强制终止
# Alt+SysRq+k  - 终止当前终端所有进程
# Alt+SysRq+t  - 显示任务状态
# Alt+SysRq+w  - 显示阻塞任务
# Alt+SysRq+p  - 显示寄存器
# Alt+SysRq+c  - 触发 panic(kdump 测试)

# 安全重启序列(记忆: Raising Elephants Is So Utterly Boring)
# Alt+SysRq+r  - 取消原始模式
# Alt+SysRq+e  - 终止进程
# Alt+SysRq+i  - 强制终止
# Alt+SysRq+s  - 同步
# Alt+SysRq+u  - 只读挂载
# Alt+SysRq+b  - 重启

# 命令行方式
$ echo s > /proc/sysrq-trigger   # 同步
$ echo u > /proc/sysrq-trigger   # 只读
$ echo b > /proc/sysrq-trigger   # 重启

实战案例

案例 1:修复 GRUB 引导

# 场景:GRUB 损坏,无法启动

# 1. 从 Live USB 启动
# 2. 挂载根分区
$ mount /dev/sda1 /mnt
# 如果有 EFI 分区
$ mount /dev/sda2 /mnt/boot/efi

# 3. 挂载虚拟文件系统
$ mount --bind /dev /mnt/dev
$ mount --bind /proc /mnt/proc
$ mount --bind /sys /mnt/sys

# 4. chroot 到系统
$ chroot /mnt

# 5. 重新安装 GRUB
# BIOS:
$ grub-install /dev/sda
# UEFI:
$ grub-install --target=x86_64-efi --efi-directory=/boot/efi

# 6. 重新生成配置
$ update-grub        # Debian/Ubuntu
$ grub2-mkconfig -o /boot/grub2/grub.cfg  # RHEL

# 7. 退出并重启
$ exit
$ reboot

案例 2:忘记 root 密码

# 方法 1: init=/bin/bash
# 1. GRUB 菜单按 'e'
# 2. 在 linux 行末添加: init=/bin/bash
# 3. Ctrl+X 启动
# 4. 重新挂载
mount -o remount,rw /
# 5. 修改密码
passwd root
# 6. SELinux 重新标记
touch /.autorelabel
# 7. 重启
exec /sbin/init

# 方法 2: rd.break
# 1. GRUB 菜单按 'e'
# 2. 在 linux 行末添加: rd.break
# 3. Ctrl+X 启动
# 4. 重新挂载 sysroot
mount -o remount,rw /sysroot
chroot /sysroot
passwd root
touch /.autorelabel
exit
exit

案例 3:修复 fstab 错误

# 场景:/etc/fstab 配置错误导致无法启动
# systemd 进入 emergency 模式

# 1. 输入 root 密码进入 emergency shell
# 2. 重新挂载为读写
mount -o remount,rw /

# 3. 检查 fstab
cat /etc/fstab
# 找到错误的行

# 4. 修复 fstab
vi /etc/fstab
# 注释掉错误的行

# 5. 验证挂载
mount -a
# 无报错则修复成功

# 6. 重启
reboot

案例 4:initramfs 缺少驱动

# 场景:升级内核后无法启动,提示找不到根分区
# 原因:initramfs 缺少 NVMe 驱动

# 1. 从旧内核启动(GRUB 菜单选择 Advanced options)

# 2. 添加驱动到 initramfs
# Debian/Ubuntu:
echo "nvme" >> /etc/initramfs-tools/modules
echo "nvme-core" >> /etc/initramfs-tools/modules
update-initramfs -u -k $(uname -r)

# RHEL/CentOS:
echo 'add_drivers+=" nvme nvme-core "' > /etc/dracut.conf.d/nvme.conf
dracut -f

# 3. 重启验证
reboot

启动流程检查清单

#!/bin/bash
# boot-check.sh - 启动健康检查

echo "=== 启动健康检查 ==="

echo ""
echo "1. 启动模式"
[ -d /sys/firmware/efi ] && echo "   UEFI" || echo "   Legacy BIOS"

echo ""
echo "2. 启动耗时"
systemd-analyze

echo ""
echo "3. 启动最慢的服务(Top 10)"
systemd-analyze blame | head -10

echo ""
echo "4. 默认 target"
systemctl get-default

echo ""
echo "5. 失败的服务"
systemctl --failed

echo ""
echo "6. GRUB 配置"
echo "   内核参数: $(cat /proc/cmdline)"

echo ""
echo "7. 内核版本"
uname -r

echo ""
echo "8. 最近启动记录"
journalctl --list-boots | tail -5

echo ""
echo "9. 上次启动的错误"
journalctl -b -1 -p err --no-pager 2>/dev/null | tail -10

echo ""
echo "10. kdump 状态"
systemctl is-active kdump 2>/dev/null || echo "   kdump 未安装/未启用"

总结

Linux 启动流程是一个精密的多阶段过程,每个阶段都可能出问题。核心要点:

  1. 理解五个阶段:固件(BIOS/UEFI)→ 引导(GRUB2)→ 内核 → initramfs → systemd,每个阶段有不同的排查方法。
  2. UEFI 是现代标准:支持 GPT 分区、安全启动、快速启动,新服务器应优先使用 UEFI。
  3. GRUB2 配置通过 /etc/default/grub:修改后必须执行 update-grub/grub2-mkconfig,不要手动编辑 grub.cfg
  4. initramfs 是启动的关键中间件:缺少驱动会导致无法挂载根分区,修改后需要重新生成。
  5. systemd 的 target 取代了 runlevelmulti-user.target 对应 runlevel 3,graphical.target 对应 runlevel 5。
  6. 服务依赖决定启动顺序Requires/Wants/After/Before 控制依赖和顺序,systemctl list-dependencies 查看依赖树。
  7. 启动优化从分析开始systemd-analyze blame 找出最慢的服务,systemd-analyze critical-chain 分析关键路径。
  8. 紧急恢复有多种方式rescue.target(最小服务)、emergency.target(无服务)、init=/bin/bash(跳过 systemd)、rd.break(initramfs 阶段)。
  9. kdump 是内核崩溃分析的基础:生产环境必须配置,崩溃后可分析转储文件定位根因。
  10. SysRq 是最后的手段:系统完全无响应时,用 REISUB 序列安全重启,避免直接断电导致数据损坏。

启动故障排查的黄金法则:从最后成功的阶段开始排查。如果 GRUB 能显示菜单,说明固件和 GRUB 正常;如果内核能加载但卡在挂载根分区,问题在 initramfs;如果 systemd 启动了但某服务失败,用 journalctl 查看具体错误。