2、SysV Init运行级别:0-6运行级别的定义与切换机制
聊到SysV Init,运行级别是个绕不开的话题。我刚开始接触Linux系统管理那会儿,觉得这玩意儿挺玄乎的——不就是个数字嘛,0到6七个数字,能玩出什么花来?后来在生产环境里栽过跟头,才明白这七个数字背后藏着大学问。
2.1 运行级别到底是什么?
说白了,运行级别就是Linux系统的一组预设状态。每个状态决定了系统启动后跑哪些服务、不跑哪些服务。你想想看,一台服务器在单用户模式下修故障,跟它在多用户模式下对外提供服务,需要的服务集合肯定不一样。
SysV Init用0到6这七个数字来定义这些状态。嗯,这里要注意——虽然数字是0到6,但实际可用的运行级别其实更多,只是约定俗成用这七个。我见过有些老系统还自定义了8、9级别,不过那是极少数情况。
2.2 0-6运行级别的标准定义
咱们先看一张表,把每个级别的含义搞清楚:
| 运行级别 | 名称 | 功能描述 | 典型用途 |
|---|---|---|---|
| 0 | 关机 | 停止所有服务,关闭系统 | 安全关机 |
| 1 | 单用户模式 | 仅root用户,无网络,最小化服务 | 系统修复、密码重置 |
| 2 | 多用户模式(无NFS) | 多用户文本界面,无网络文件系统 | Debian系默认多用户级别 |
| 3 | 完整多用户模式 | 多用户文本界面,带网络服务 | 服务器标准运行级别 |
| 4 | 未使用/自定义 | 保留给用户自定义 | 特殊场景定制 |
| 5 | 图形化多用户模式 | 多用户文本界面 + 图形界面 | 桌面系统默认级别 |
| 6 | 重启 | 停止所有服务,重启系统 | 安全重启 |
这张表看着简单,但实际用起来坑不少。我记得有一次,同事在RHEL 6上把默认运行级别改成了0,结果服务器一开机就关机……嗯,这种低级错误其实挺常见的。
2.3 运行级别的切换机制
运行级别切换,核心靠的是init进程。当你在终端敲下init 3或者telinit 3时,init进程会做三件事:
- 读取
/etc/inittab:找到目标运行级别对应的配置 - 终止旧服务:停止当前级别特有的服务
- 启动新服务:启动目标级别需要的服务
具体怎么操作?看个例子:
# 查看当前运行级别
runlevel
# 切换到运行级别3(文本多用户模式)
init 3
# 或者用telinit,效果一样
telinit 3
# 查看切换后的状态
runlevel
输出结果一般是这样的:
N 5 # 之前是5,现在还是5(没切换)
5 3 # 之前是5,现在切换到了3
这里有个细节——runlevel命令显示的是上一个和当前的运行级别。第一个字母是N,表示没有上一个级别(刚开机)。
2.4 服务启动与停止的脚本机制
运行级别切换时,服务怎么启停?这就要说到/etc/rc.d/目录下的脚本了。每个运行级别对应一个子目录:
/etc/rc.d/rc0.d/ # 关机级别
/etc/rc.d/rc1.d/ # 单用户级别
/etc/rc.d/rc2.d/ # 多用户级别
/etc/rc.d/rc3.d/ # 完整多用户
/etc/rc.d/rc4.d/ # 自定义
/etc/rc.d/rc5.d/ # 图形化
/etc/rc.d/rc6.d/ # 重启
每个目录里都是符号链接,指向/etc/init.d/下的实际脚本。链接的命名规则很有意思:
- S开头:启动服务(Start),后面跟两位数字决定顺序
- K开头:停止服务(Kill),同样跟两位数字
举个例子:
# 在rc3.d目录下
S10network # 启动网络服务,顺序10
S55sshd # 启动SSH服务,顺序55
K90network # 停止网络服务,顺序90
数字越小,执行越早。我个人习惯把核心服务(比如网络、磁盘)放在前面,应用服务放后面。这样依赖关系不容易出问题。
2.5 切换时的实际流程
当执行init 3时,init进程会:
- 对比当前级别和目标级别的服务列表
- 找出需要停止的服务(当前级别有,目标级别没有)
- 找出需要启动的服务(目标级别有,当前级别没有)
- 先执行K开头的脚本(停止服务)
- 再执行S开头的脚本(启动服务)
这个顺序很重要。我曾经遇到过一个问题:切换运行级别时,某个服务没停干净,新服务启动就报端口冲突。后来排查发现,是K脚本的执行顺序没处理好。
init 3执行到一半时按了Ctrl+C,结果系统服务状态全乱了,最后只能进单用户模式手动修复。
2.6 默认运行级别的设置
系统启动时默认进入哪个级别?这由/etc/inittab文件控制:
# 在/etc/inittab中设置默认运行级别
id:3:initdefault: # 默认进入运行级别3
对于RHEL/CentOS 6及更早版本,直接改这个文件就行。但要注意——有些发行版(比如Debian/Ubuntu)的/etc/inittab可能不存在,它们用/etc/init/rc-sysinit.conf或者/etc/default/rcS来控制。
grep initdefault /etc/inittab。没输出的话,八成是用了Upstart或者Systemd。
2.7 实际项目中的经验
我在维护一个老旧的RHEL 5集群时,遇到过这么个场景:某台服务器需要临时切换到单用户模式修磁盘,但修完后忘了切回来,结果跑了三天单用户模式,业务全挂了。从那以后,我养成了一个习惯——每次切换运行级别后,都用runlevel确认一下当前状态。
还有一次,我在做系统安全加固时,把默认运行级别从5改成了3(去掉图形界面)。结果第二天同事说服务器起不来——原来他依赖图形界面做监控。嗯,这种沟通问题其实比技术问题更难搞。
2.8 运行级别的现代替代方案
说实话,SysV Init的运行级别机制在Systemd时代已经不太常用了。Systemd用target替代了运行级别,但概念上是一脉相承的:
| SysV运行级别 | Systemd Target |
|---|---|
| 0 | poweroff.target |
| 1 | rescue.target |
| 2 | multi-user.target |
| 3 | multi-user.target |
| 4 | multi-user.target |
| 5 | graphical.target |
| 6 | reboot.target |
但别以为SysV Init就过时了。很多嵌入式系统、老旧服务器、以及某些定制化Linux发行版,依然在用这套机制。我建议你至少把0、1、3、5、6这五个级别的含义记牢——面试经常考,实际工作中也经常用。