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进程要同时管理几十上百个配置行,总得有个办法区分它们吧?

我个人习惯用l0l1这样的命名,清晰明了。但有些发行版会用01这样的数字。都可以,只要不重复就行。

注意:标识符不能重复。如果两个配置行用了相同的id,Init进程会忽略后面那一行。我曾经见过有人把两个getty配置写成了相同的id,结果只有一个终端能登录,另一个死活起不来。

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会马上再拉起来一个。

小技巧:如果你想让某个服务「打不死」,可以用respawn。但要注意,如果进程频繁崩溃(比如一秒钟内重启了10次),Init会认为系统有问题,暂停respawn并给出警告。这是为了防止CPU被无限占用。

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的服务)。

核心要点:运行级别切换的本质,就是执行一系列脚本。这些脚本里通常调用startstop参数,来控制服务的启停。

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是波特率,tty1tty6是终端设备名,linux是终端类型。你按Ctrl+Alt+F1F6,就是在这些终端之间切换。

这里有个小技巧:如果你只需要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进程重新读取配置。有两种方式:

  1. 执行telinit q:告诉Init重新读取配置文件,不会影响当前运行状态。
  2. 执行init q:效果同上,inittelinit的符号链接。

我个人习惯用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的核心。下一章,我们会深入探讨运行级别切换时,那些脚本到底干了些什么。