4、SysV Init配置文件:/etc/inittab文件逐行深度解析
聊到SysV Init,绕不开的就是/etc/inittab这个文件。说实话,我早年刚接触Linux时,觉得这玩意儿就是一堆奇怪的缩写和数字。后来在项目中吃过亏——有一次线上服务器重启后,某个关键服务死活起不来,排查了半天,最后发现是/etc/inittab里一行配置写错了。嗯,从那以后,我对这个文件再也不敢马虎了。
今天我们就来逐行拆解这个文件。我会带着你,一行一行地看,每一行是什么意思,背后有什么讲究。
4.1 文件整体结构
一个典型的/etc/inittab文件,大概长这样:
# inittab This file describes how the INIT process should set up
# the system in a certain run-level.
#
# Author: Miquel van Smoorenburg, <miquels@drinkel.nl.mugnet.org>
# Modified: Patrick J. Volkerding, <volkerdi@slackware.com>
id:3:initdefault:
# System initialization.
si::sysinit:/etc/rc.d/rc.S
# Runlevel 0 is halt.
l0:0:wait:/etc/rc.d/rc.0
# Runlevel 1 is single-user.
l1:1:wait:/etc/rc.d/rc.1
# Runlevel 2 is multi-user.
l2:2:wait:/etc/rc.d/rc.2
# Runlevel 3 is multi-user with networking.
l3:3:wait:/etc/rc.d/rc.3
# Runlevel 4 is unused.
l4:4:wait:/etc/rc.d/rc.4
# Runlevel 5 is X11.
l5:5:wait:/etc/rc.d/rc.5
# Runlevel 6 is reboot.
l6:6:wait:/etc/rc.d/rc.6
# What to do at the "3" runlevel.
# Run gettys in standard runlevels
c1:12345:respawn:/sbin/agetty 38400 tty1 linux
c2:12345:respawn:/sbin/agetty 38400 tty2 linux
c3:12345:respawn:/sbin/agetty 38400 tty3 linux
c4:12345:respawn:/sbin/agetty 38400 tty4 linux
c5:12345:respawn:/sbin/agetty 38400 tty5 linux
c6:12345:respawn:/sbin/agetty 38400 tty6 linux
# End of file
你看,这个文件其实不复杂。每一行由冒号分隔成四个字段。我习惯把这四个字段叫做:标识符、运行级别、动作、进程。
4.2 字段格式详解
每一行的标准格式是:
id:runlevels:action:process
我们一个一个来看。
4.2.1 id:标识符
第一个字段是标识符,最多四个字符。它唯一标识这一行配置。说白了,就是给这一行起个名字,方便Init进程内部管理。你想想看,Init进程要同时管理几十上百个配置行,总得有个办法区分它们吧?
我个人习惯用l0、l1这样的命名,清晰明了。但有些发行版会用0、1这样的数字。都可以,只要不重复就行。
4.2.2 runlevels:运行级别
第二个字段指定这一行在哪些运行级别下生效。可以是一个数字,也可以是多个数字连写,比如12345。如果为空,表示对所有运行级别都生效。
运行级别一共有7个(0-6),每个都有特定含义:
| 运行级别 | 含义 | 说明 |
|---|---|---|
| 0 | 关机 | 系统停止运行 |
| 1 | 单用户模式 | 维护模式,只有root能登录 |
| 2 | 多用户模式(无网络) | 某些发行版用这个 |
| 3 | 完整多用户模式 | 有网络,字符界面 |
| 4 | 未使用/自定义 | 留给用户自己定义 |
| 5 | X11图形界面 | 启动X Window |
| 6 | 重启 | 系统重新启动 |
这里有个坑:运行级别0和6是特殊级别。Init进程进入这两个级别后,会执行关机或重启操作,然后就不会再继续运行了。所以,l0:0:wait:/etc/rc.d/rc.0这一行执行完后,系统就halt了,不会再执行后面的行。
4.2.3 action:动作类型
第三个字段是动作类型,它告诉Init进程「怎么执行」后面的进程。这是整个inittab最核心的部分。常用的动作类型有这些:
| 动作 | 含义 | 说明 |
|---|---|---|
| initdefault | 默认运行级别 | 系统启动时进入哪个级别 |
| sysinit | 系统初始化 | 在进入任何运行级别前执行 |
| wait | 等待执行 | Init等待该进程结束后才继续 |
| respawn | 重新生成 | 进程结束后自动重启 |
| once | 执行一次 | 只执行一次,不等待 |
| boot | 启动时执行 | 系统启动时执行,忽略运行级别 |
| bootwait | 启动时等待 | 系统启动时执行,等待结束 |
| powerfail | 电源故障 | 收到SIGPWR信号时执行 |
| powerokwait | 电源恢复 | 电源恢复后执行 |
| ctrlaltdel | Ctrl+Alt+Del | 按下组合键时执行 |
这里面,respawn是最有意思的。它会让Init进程一直盯着这个进程,一旦进程退出,就立刻重新启动它。你想想看,getty进程就是靠这个机制,才能保证你随时都能登录系统。如果getty挂了,Init会马上再拉起来一个。
4.2.4 process:要执行的进程
第四个字段就是实际要执行的命令或脚本。可以是绝对路径,也可以带参数。比如/sbin/agetty 38400 tty1 linux,就是启动一个getty进程,在tty1上提供登录服务。
这里有个细节:Init进程在执行这些命令时,会以root权限运行。所以,如果你在inittab里写了一个脚本,它默认就是root身份。嗯,权限问题要小心。
4.3 逐行解读示例文件
现在我们回到最开始的示例文件,一行一行地看。
4.3.1 默认运行级别
id:3:initdefault:
这一行告诉Init:系统启动后,默认进入运行级别3。注意,这里第四个字段是空的,因为initdefault动作不需要执行任何进程,它只是一个配置声明。
如果你把这里的3改成5,系统启动后就会直接进入图形界面。我个人习惯在服务器上设为3,在桌面系统上设为5。
4.3.2 系统初始化
si::sysinit:/etc/rc.d/rc.S
这一行的运行级别字段是空的,表示对所有级别都生效。sysinit动作保证它在进入任何运行级别之前执行。/etc/rc.d/rc.S是系统初始化脚本,负责挂载文件系统、设置主机名、激活swap等基础工作。
我记得有一次,某个系统启动时总是报文件系统错误,排查了半天,最后发现是rc.S脚本里挂载顺序写错了。嗯,这种基础脚本出了问题,影响面特别大。
4.3.3 运行级别切换脚本
l0:0:wait:/etc/rc.d/rc.0
l1:1:wait:/etc/rc.d/rc.1
l2:2:wait:/etc/rc.d/rc.2
l3:3:wait:/etc/rc.d/rc.3
l4:4:wait:/etc/rc.d/rc.4
l5:5:wait:/etc/rc.d/rc.5
l6:6:wait:/etc/rc.d/rc.6
这7行定义了每个运行级别对应的脚本。动作是wait,意思是Init会等待这些脚本执行完毕,才继续后面的操作。每个脚本负责启动或停止该运行级别对应的服务。
举个例子,当你从运行级别3切换到运行级别5时,Init会先执行rc.3(停止级别3的服务),然后执行rc.5(启动级别5的服务)。
start和stop参数,来控制服务的启停。
4.3.4 虚拟终端配置
c1:12345:respawn:/sbin/agetty 38400 tty1 linux
c2:12345:respawn:/sbin/agetty 38400 tty2 linux
c3:12345:respawn:/sbin/agetty 38400 tty3 linux
c4:12345:respawn:/sbin/agetty 38400 tty4 linux
c5:12345:respawn:/sbin/agetty 38400 tty5 linux
c6:12345:respawn:/sbin/agetty 38400 tty6 linux
这6行配置了6个虚拟终端。运行级别字段是12345,意味着在级别1到5下,这些终端都会启动。动作是respawn,所以即使你退出登录,getty也会立刻重新启动,等待下一次登录。
参数38400是波特率,tty1到tty6是终端设备名,linux是终端类型。你按Ctrl+Alt+F1到F6,就是在这些终端之间切换。
这里有个小技巧:如果你只需要3个终端,可以把后面3行删掉或注释掉。这样可以节省一点系统资源。不过说实话,现在内存都很大,这点资源基本可以忽略。
4.4 特殊配置行
除了上面这些常规配置,inittab里还有一些特殊用途的行。
4.4.1 Ctrl+Alt+Del处理
ca::ctrlaltdel:/sbin/shutdown -t1 -a -r now
这一行定义了按下Ctrl+Alt+Del时的行为。通常执行关机或重启命令。在服务器上,我建议把这一行注释掉,防止有人误操作导致系统重启。
4.4.2 电源故障处理
pf::powerfail:/sbin/shutdown -f +5 "Power Failure; System Shutting Down"
这一行用于UPS电源故障时自动关机。当UPS发出低电量信号时,Init会收到SIGPWR信号,然后执行关机操作。嗯,这个配置在数据中心里很常见。
4.5 修改inittab后的生效方式
修改了/etc/inittab后,需要让Init进程重新读取配置。有两种方式:
- 执行
telinit q:告诉Init重新读取配置文件,不会影响当前运行状态。 - 执行
init q:效果同上,init是telinit的符号链接。
我个人习惯用telinit q,因为语义更清晰——「告诉Init重新加载配置」。
inittab后一定要执行telinit q,否则修改不会生效。我曾经见过有人改了文件后直接重启系统,结果发现配置没生效——因为重启时Init会重新读取文件,但如果你改错了,系统可能起不来。所以,改完后先用telinit q测试一下,确认没问题再重启。
4.6 常见问题与避坑指南
最后,分享几个我在项目中遇到过的坑:
- 标识符重复:前面说过,重复的id会导致后面一行被忽略。检查时可以用
grep '^[a-z]' /etc/inittab | cut -d: -f1 | sort | uniq -d来找出重复项。 - 运行级别写错:比如把
l3:3:wait写成了l3:4:wait,结果切换到级别3时,这个脚本不会执行。我建议用telinit 3手动切换测试一下。 - 进程路径错误:如果
process字段里的路径写错了,Init会报错,但不会影响其他行。可以用ps aux | grep init查看Init进程的日志。 - 忘记执行
telinit q:这个最常见,改完文件后一定要记得执行。
好了,/etc/inittab的逐行解析就到这里。这个文件虽然看起来简单,但每一行都关系到系统的启动流程。理解了它,你就掌握了SysV Init的核心。下一章,我们会深入探讨运行级别切换时,那些脚本到底干了些什么。