runlevel 与 target

已修订更新于

首发于

上一篇把 systemd 拆成了 unit 类型和文件后缀。这篇只盯一个历史包袱:运行级别(runlevel)——它从哪来、曾经管什么、和今天的 .target 是什么关系、数字 0–6 在现实里还值不值得背。

抛开 SysV init 的细节语法,先抓住一件事:运行级别从来不是「内核模式」,而是 用户态 init 对「整机处于哪种工作姿态」的约定编号。

读前可先备着的入口


一、问题从哪来:开机之后系统要成什么样

一台 Unix/Linux 机器启动后,至少要回答:

早期没有 systemd 那张精细依赖图时,习惯做法是:定义几种 离散状态,每种状态对应一份「该启哪些脚本」的清单。状态用数字编号——就是 runlevel。

开机 → init(PID 1) → 进入某个 runlevel
                         → 按该级别的脚本目录启停服务

systemd 换成了:

开机 → systemd(PID 1) → 以某 default.target 为锚
                         → 按依赖图拉起一堆 unit

目标同类,模型不同:前者是「编号 + 脚本目录」,后者是「命名 target + unit 依赖」。


二、历史渊源:SysV init 与数字级别

1. 名字从哪来

System V(读作「System Five」)是 AT&T 的 Unix 分支;其 init 及 /etc/inittab 风格,被后来大量商业 Unix 和 Linux 发行版沿用,常称 SysV init。

Linux 早期主流也是这套:/etc/inittab 指定默认 runlevel;每个级别在 /etc/rc.d/rcN.d/(或类似路径)里放一串 S* / K* 脚本,进入该级别时按序启动或杀掉服务。

2. 经典编号约定(常见 Linux 习惯)

具体哪一级跑图形,各发行版曾有过出入;下面是后来很常见、教材里也常写的 Linux 习惯表(不是 POSIX 铁律):

Runlevel 常见含义
0 关机(halt)
1 单用户 / 维护模式(可对应 rescue 一类)
2 多用户,常不带网络(部分发行版如此定义)
3 多用户 + 网络,字符界面(服务器默认常见)
4 未定义 / 自定义
5 多用户 + 网络 + 图形界面
6 重启(reboot)

要点:

3. 它实际解决了什么

在依赖还靠「脚本排序编号」(S10network、S55sshd…)的年代,runlevel 提供了:

  1. 一种粗粒度模式切换(维护 / 服务器 / 桌面)
  2. 一套启停清单的目录约定(进 5 就跑 rc5.d)
  3. 运维口头语(「切到 3」「别开图形」)

它管不了(至少管不好)的是:细依赖、并行启动、socket 激活、按挂载点生成 unit——这些正是后来 systemd 用依赖图补上的。


三、systemd 怎么接盘:target 代替编号

1. target 是什么

.target 是一种 几乎不执行程序 的 unit:它的意义是当 同步点 / 愿望清单——「当系统声称到达这个目标时,下面挂着的那些 unit 应该已经起来(或按 Wants/Requires 尽量起来)」。

日常最重要的几个:

target 大致角色
poweroff.target 关机
rescue.target 救援/维护(近似旧单用户)
multi-user.target 多用户、通常无图形(近似旧 3)
graphical.target 在 multi-user 之上再要图形(近似旧 5)
reboot.target 重启
emergency.target 更狠的急救壳,比 rescue 更靠前、服务更少

默认启动目标一般是:

systemctl get-default
# 常见:graphical.target 或 multi-user.target

改默认(等价于昔日改 inittab 里的默认 runlevel):

sudo systemctl set-default multi-user.target

临时切过去(近似昔日 telinit 3):

sudo systemctl isolate multi-user.target

isolate 很刺:会停掉不属于该目标的一堆东西,慎用在生产会话里。

2. 编号兼容:systemd 故意留了桥

为了让老文档、老脚本、老肌肉记忆还能用,systemd 提供了 runlevel 别名 target,大致映射如下:

旧 runlevel systemd 别名(示意) 通常指向
0 runlevel0.target poweroff.target
1 runlevel1.target rescue.target
2 runlevel2.target multi-user.target(多发行版如此)
3 runlevel3.target multi-user.target
4 runlevel4.target multi-user.target
5 runlevel5.target graphical.target
6 runlevel6.target reboot.target

因此你仍可能看到:

runlevel          # 兼容命令,打印「上一个」和「当前」风格的数字
systemctl isolate runlevel3.target

但 新写法应优先用名字:multi-user.target / graphical.target。数字只是兼容层,语义以发行版的符号链接为准。

3. 一张对照图

SysV 时代                         systemd 时代
─────────                         ────────────
inittab 默认级别          →       default.target → 某 .target
/etc/rcN.d/ 脚本清单      →       Wants=/Requires= 依赖图
telinit N / init N        →       systemctl isolate xxx.target
runlevel 3 / 5 口头语     →       multi-user / graphical
runlevel 0 / 6            →       poweroff / reboot

上一篇说的「systemd 管了那么多 service / mount / timer」,在这里收束成一句:

运行级别从来不是独立魔法,只是「该启用哪一组服务」的标签;target 把标签从数字换成了可组合的依赖锚点。


四、现实作用:今天还要不要惦记 runlevel

1. 仍然有用的部分

2. 已经变弱或不该再依赖的部分

3. 和「服务类型」的关系(回扣上一篇)

没有 target,一堆 unit 仍能跑,但缺少「开机默认要到哪」「isolate 时以谁为边界」的锚。
没有各种 service/mount,target 只是空篮子。


五、文章能用在哪:几类真实场景

1. 服务器只要字符界面,别默认进桌面

装完整桌面后 SSH 上去却拖着一堆图形栈——常见解法不是天天 isolate,而是改默认目标:

systemctl get-default
sudo systemctl set-default multi-user.target
# 重启后验证
systemctl get-default
systemctl status graphical.target   # 通常应 inactive / not reached

对应旧口头语:「改成 runlevel 3」。

2. 读老教程 / 考纲里的「切到 5」「telinit 3」

把数字翻译成 target 即可,不必在 systemd 机器上真去背 rc 目录:

旧说法 优先写成
默认 runlevel 3 systemctl set-default multi-user.target
默认 runlevel 5 systemctl set-default graphical.target
telinit 3 / init 3 systemctl isolate multi-user.target(慎用)
telinit 6 systemctl reboot(或 isolate reboot.target)

3. 系统起不来:救援 vs 急救

GRUB/编辑内核参数时常见写法(发行版文案可能略有出入):

systemd.unit=rescue.target
# 或
systemd.unit=emergency.target

进救援后修 fstab、禁用坏 unit、改回 default.target,再重启。细节以发行版「救援模式」文档为准。

4. 排障:默认目标指错了、被别名绕晕了

systemctl get-default
ls -l /etc/systemd/system/default.target
systemctl list-dependencies multi-user.target
systemctl list-dependencies graphical.target --after
# 兼容层仍在时:
runlevel
ls -l /lib/systemd/system/runlevel*.target

看符号链接比背数字更靠谱:你的发行版把 runlevel3.target 链到谁,以本机为准。

5. 什么时候不该用这篇的知识硬套


六、实操极简备忘

# 当前默认目标
systemctl get-default

# 设为无图形多用户(近似旧 runlevel 3)
sudo systemctl set-default multi-user.target

# 设为图形(近似旧 runlevel 5)
sudo systemctl set-default graphical.target

# 谁被挂进 graphical?
systemctl list-dependencies graphical.target

# 兼容:看「runlevel」风格输出(若仍提供)
runlevel

七、收束

  1. Runlevel 来自 SysV 风格 init:用数字标记整机工作姿态,用 rc 目录脚本落实。
  2. systemd 用 target 接手同一问题,并把关机/重启/救援/多用户/图形都变成可依赖的 unit。
  3. 数字 0–6 仍以别名形式活着,方便兼容;新知识应建立在 具名 target 上。
  4. 现实中你真正常做的,往往只是:选对 default.target,必要时进 rescue/emergency,其余交给上一篇那张 unit 依赖图。

下一篇把日常敲的一堆 *ctl 收成一张家族地图:PID 1、配套守护进程、遥控器各自站哪一层——systemd 家族与 *ctl 地图。


参考资料

官方手册

本机:man bootup、man systemd.special、man systemctl。

发行版与背景阅读

站内相关

评论

可对整篇留言,或划选正文中的句子后评论该段。

登录后即可发表评论。

  • 还没有公开评论。
↑