4. 常用启动参数详解(中):init、systemd.unit、single、emergency

系统初始化阶段,是内核交棒给用户空间的关键一步。说白了,就是内核启动完,得有人来拉起所有服务、挂载文件系统、配置网络。谁来做这件事?就是 init 进程。这一章我们聊聊跟初始化相关的几个核心参数,都是我平时排查启动故障时最常用的。

4.1 init 参数:指定你的 1 号进程

内核启动的最后一步,会执行 /sbin/init 作为第一个用户态进程(PID 1)。但你可以通过 init= 参数强行指定别的程序。

语法: init=/path/to/program

默认值: /sbin/init(通常是 systemd 的软链接)

我遇到过几次系统因为 /sbin/init 损坏而无法启动的情况。这时候用 init=/bin/bash 直接进一个 shell,就能修复文件系统或者重装软件包。

实用场景:

  • 救援模式:init=/bin/bash 或 init=/bin/sh
  • 临时替换 init 系统:比如从 systemd 切到 sysvinit 做测试
  • 容器场景:指定自定义的初始化程序

注意: 用 init=/bin/bash 启动后,很多服务不会自动运行。你需要手动挂载 /proc、/sys、/dev,否则很多命令会报错。我曾经吃过这个亏,进去后 mount 都跑不了,折腾了半天才发现没挂 /proc。

4.2 systemd.unit:直接指定启动目标

如果你的系统用的是 systemd(现在绝大多数发行版都是),systemd.unit= 参数可以让你直接指定启动到哪个 target。这比进系统后再 systemctl isolate 要快得多。

参数值 对应目标 说明
systemd.unit=multi-user.target 多用户文本模式 不启动图形界面,适合服务器
systemd.unit=graphical.target 图形界面模式 启动桌面环境
systemd.unit=rescue.target 单用户救援模式 基础文件系统挂载,root shell
systemd.unit=emergency.target 紧急模式 最精简环境,只挂载根文件系统

我个人习惯在调试启动问题时,直接在 GRUB 命令行里加上 systemd.unit=rescue.target。这样既能拿到一个可用的 shell,又比 emergency 模式多了一些基础服务(比如网络)。

4.3 single:传统单用户模式

single 参数是 sysvinit 时代的遗产,现在在 systemd 系统上,它会被映射成 rescue.target。用法很简单,直接在内核参数行末尾加上 single 或 1(数字 1 也是同样的效果)。

等价关系:

  • single → systemd.unit=rescue.target
  • 1 → systemd.unit=rescue.target
  • s → systemd.unit=rescue.target

嗯,这里要注意:single 模式会提示你输入 root 密码。如果你忘了 root 密码,那就得用 init=/bin/bash 或者 rd.break(针对 dracut 的 initramfs)来绕过了。

4.4 emergency:最后的救命稻草

emergency 参数对应 systemd 的 emergency.target。这是比 rescue 更精简的模式——只挂载根文件系统(只读),不启动任何服务,连网络都没有。你得到的就是一个孤零零的 root shell。

什么时候用 emergency?

  • 根文件系统损坏,需要 fsck
  • 关键系统服务导致启动死循环
  • 需要在不加载任何额外模块的情况下修复系统

我曾经有一次遇到 /etc/fstab 配置错误,导致系统在挂载分区时卡死。用 emergency 进去后,根文件系统是只读的,得先 mount -o remount,rw / 才能修改 fstab。这个细节很多人不知道,进去后直接 vi /etc/fstab 发现保存不了,就懵了。

4.5 各初始化模式的对比

模式 内核参数 根文件系统 网络 多用户 适用场景
正常启动 (无) 读写 ✓ ✓ 日常使用
救援模式 single / 1 读写 ✗ ✗ 修复配置、重置密码
紧急模式 emergency 只读 ✗ ✗ 修复根文件系统、抢救数据
自定义 init init=/bin/bash 取决于脚本 ✗ ✗ 极端修复、init 损坏

4.6 实战:用 systemd.unit 跳过故障服务

说个真实案例。有一次我维护的服务器,某个自定义服务在启动时总是 segfault,导致 systemd 不断尝试重启,整个启动过程被拖慢了 5 分钟。怎么快速进系统?

  1. 重启,在 GRUB 菜单按 e 编辑启动项
  2. 在 linux 开头的行末尾加上 systemd.unit=multi-user.target
  3. 按 Ctrl+X 启动
  4. 进系统后,用 systemctl disable 故障服务 禁用它
  5. 再正常重启,问题解决

你看,一个参数就绕过了故障点,不用进救援模式那么麻烦。

4.7 知识体系图:初始化参数决策流程

下面这张图总结了不同场景下该选哪个初始化参数。我建议你把它截图保存,或者记在笔记本上——线上出问题时,没时间翻文档。

初始化参数决策流程 系统无法正常启动 是否需要网络/多用户? 是 systemd.unit=multi-user.target 否 single / 1 / rescue.target emergency.target 正常启动,排查服务 提示 如果 rescue 模式也无法修复,尝试 init=/bin/bash 进入后先执行 mount -o remount,rw / 获取写权限

4.8 避坑指南

最后分享几个我踩过的坑,你遇到了能少走弯路。

  • init=/bin/bash 后忘记 remount: 进去后第一件事就是 mount -o remount,rw /,否则所有修改都写不进去。
  • single 模式需要 root 密码: 如果你忘了密码,用 rd.break 或者 init=/bin/bash 绕过。
  • emergency 模式下没有 /dev: 有些 initramfs 在 emergency 模式下不会自动创建设备节点。如果要用 dd 或 fdisk,先手动 mount -t devtmpfs none /dev。
  • systemd.unit= 参数拼写: 是 systemd.unit,不是 systemd-unit,也不是 systemd_unit。我见过有人因为这个下划线折腾了半小时。

这些初始化参数,说白了就是给你一把钥匙,让你在系统出问题时还能开门进去修。平时多练练,真到线上故障的时候,你就能从容应对了。