🐧 第05章 · 系统启动与引导

Systemd 启动流程 systemctl enable

系统启动与引导

服务器开机后到底发生了什么、为什么有的服务开机自启有的没有、为什么启动卡在某一秒?这一章梳理从 BIOS/UEFI 到 systemd 的完整引导流程,并教你用 systemd-analyze 定位启动慢点、管理多目标(target)。

开机流程:从固件到 systemd

一台 Ubuntu 24.04 服务器(BIOS/UEFI + GRUB + systemd)的启动链条:

BIOS/UEFI 固件
  → 读取 GRUB 引导程序(如 /boot/grub)
  → GRUB 加载内核(vmlinuz)与 initramfs
  → 内核解压,接管硬件
  → 启动 init(第一个进程),systemd 的 PID=1
  → systemd 并行拉起各服务
  → 达到默认 target,登录提示符出现
阶段 谁来干 出问题表现
固件自检 BIOS/UEFI 卡 Logo、黑屏
引导程序 GRUB Grub 菜单、initrd not found
内核启动 vmlinuz + initramfs 内核 panic、找不到根盘
服务拉起 systemd 某服务报 failed、卡在 [ OK ] 某步

易错 内核与 initramfs 放在 /boot(本机 /dev/sda16,913M)。initramfs 里含有加载根文件系统所需的驱动(如 ext4、virtio),如果把 /boot 写满或删了内核版本,重启就会进 rescue 模式 —— 对 /boot 的操作必须谨慎。

systemd 的核心概念:unit 与 target

Systemd 用 unit 统一管理所有"开机要干的事"。常见 unit 类型:

  • .service —— 服务(如 nginx.service);
  • .target —— 目标/启动级别(如 multi-user.target);
  • .socket、.timer、.path —— 套接字激活、定时器、路径触发。
systemctl list-units --type=service --state=running       # 正在运行的 service
systemctl list-unit-files --type=service | grep enabled   # 开机自启的 service

易错 systemctl list-units 默认只显示"已加载/活动"的 unit,看不到已禁用的;要看全部自启设置(含 disabled/static)必须用 list-unit-files。

开机自启:default target 与 enable

systemd 开机会启动到"默认 target"。Ubuntu 桌面版默认 graphical.target(GUI),服务器版默认 multi-user.target(纯终端)。

systemctl get-default                # 查看默认 target
systemctl set-default multi-user.target   # 设为纯命令行(服务器常见)
systemctl isolate multi-user.target   # 立即切换到该 target

第 4 章说过 systemctl enable 设置开机自启——它的本质是建立符号链接,把 unit 链接进某个 target 的 .wants/ 目录:

# enable 背后做了什么(以多用户 target 为例)
ls /etc/systemd/system/multi-user.target.wants/ | grep -i nginx
# 会看到 nginx.service -> /lib/systemd/system/nginx.service 的软链接

enable / disable 就是增删这些链接。mask 则更彻底(链接到 /dev/null)。

定位启动慢点:systemd-analyze

服务能不能快速起来、卡在哪一步,用 systemd-analyze 分析:

systemd-analyze time             # 内核与用户空间各花了多少
systemd-analyze blame            # 按启动耗时排序,看谁拖慢了
systemd-analyze critical-chain   # 关键链:谁阻塞了启动
systemd-analyze blame | head     # 只看最慢的几项

易错 systemd-analyze 读的是上一次启动的数据(存于 journal 里),刚重启后立刻看数字可能是 0。blame 结果的单位是 ms(毫秒),xxx.ms 即 0.xxx 秒。

启动日志与失败排查

服务开机启动失败,看 journal 的 boot 级日志:

journalctl -b                 # 本次开机以来的日志
journalctl -b -p err          # 只看错误级别
journalctl -b -u bangdream    # 开机启动 bangdream 这一段
journalctl -b -u nginx -e    # 时间从早到晚,-e 跳到尾部
journalctl --list-boots       # 列出所有历史开机记录
journalctl -b -1 -u mysql     # 看上一次开机的 mysql 日志(-1)

提示 服务启动了但立刻 failed,先看 journalctl -u <服务> -e 的报错,再 systemctl reset-failed <服务> 清除失败状态(否则 status 一直显示 failed 影响判断),最后 systemctl start 重试。

救援与紧急模式

系统开机进不去(fstab 写错、关键服务挂了),进 rescue / emergency 模式修复:

systemctl rescue        # 进入救援模式(单用户,少服务)
systemctl emergency     # 进入紧急模式(最小环境,几乎不含服务)

在 GRUB 菜单按 e 编辑内核行,在 ro quiet splash 后追加 systemd.unit=rescue.target 或 systemd.unit=emergency.target 也能强制进入。此时挂载根分区读写,修复 /etc/fstab 等即可。

易错 emergency 模式几乎不加载任何服务,连 fsck 和手动命令都可能受限。能进 rescue 就先用 rescue;emergency 是最后手段。修复完 exec /sbin/init 或 systemctl reboot 重启。

案例:服务没有开机自启,重启后 Nginx 不见了

systemctl is-enabled nginx     # 确认是否为 enabled
systemctl enable --now nginx   # 设为自启并立即拉起
# 验证
systemctl is-active nginx && reboot -n    # 可选:重启后确认

易错 排查"重启后服务没起来",先 systemctl status 看是不是 enable 漏了,再看 journalctl -b -u <服务> 是不是启动时报错。很多"服务没自启"其实是启动时报错失败,不是没 enable。

案例:启动卡顿排查(本机示例)

假设本机开机偏慢,用 blame 定位:

systemd-analyze blame | head -10
# 常见大头:networkmanager 几十秒 或 某个挂载等待、Redis 初始化等待
# 针对网络等待可考虑把不需要立刻联网的服务改为 不依赖 network-online.target

易错点

  • /boot 写满或删内核:直接进不了系统。
  • 改 fstab 不 mount -a 验证:重启卡 rescue。
  • list-units 看不出"没自启"的服务:用 list-unit-files。
  • blame 读的是上一次启动数据:刚重启完数字为 0。

相关命令

systemctl、systemd-analyze、journalctl、systemctl-get-default、systemctl-set-default、systemctl rescue、lsblk、grub-editenv。