SysVinit
在基于 SysVinit 的系统中,init 是 Linux 内核加载后执行的第一个进程。内核使用的默认 init 程序是 /sbin/init
,由 systemd-sysvcompat(默认在新安装的系统中,参见 systemd)或 sysvinitAUR 提供。在本文中,init 始终指代 sysvinit。
inittab 是 init 的启动配置文件,位于 /etc
中。它包含 init 在进入特定运行级别时要运行的程序和脚本的指令。
虽然基于 SysVinit 的 Arch 系统确实使用 init,但大部分工作都委托给 #主启动脚本。本文重点介绍 init 和 inittab。
安装
从 AUR 安装 sysvinitAUR initscripts-forkAUR。此步骤将移除 systemd-sysvcompat,并且您将在重启后使用 sysvinit。要恢复 systemd,请将 init=/usr/lib/systemd/systemd
附加到内核命令行。
在 迁移到 systemd 之前,Arch Linux 中打包的 init 脚本快照可在 arch-rcscripts 中找到。 有关更新软件包的支持,请参阅 #编写 rc.d 脚本。
有关通用配置步骤,请参阅 Init#配置。
init 和 inittab 概述
init 始终是进程 1,除了管理一些交换空间外,它是所有其他进程的父进程。您可以使用 pstree
命令了解 init 在系统进程层次结构中的位置
$ pstree -Ap
init(1)-+-acpid(3432) |-crond(3423) |-dbus-daemon(3469) |-gpm(3485) |-mylogin(3536) |-ngetty(3535)---login(3954)---zsh(4043)---pstree(4326) |-polkitd(4033)---{polkitd}(4035) |-syslog-ng(3413)---syslog-ng(3414) `-udevd(643)-+-udevd(3194) `-udevd(3218)
除了通常的系统初始化(顾名思义)之外,init 还处理重启、关机和启动进入恢复模式(单用户模式)。为了支持这些,inittab 将条目分组到不同的运行级别中。Arch 使用的运行级别为 0 表示停止,1(别名为 S)表示单用户模式,3 表示正常启动(多用户模式),5 表示 X,6 表示重启。其他发行版可能采用其他约定,但 0、1 和 6 的含义是通用的。
执行时,init 扫描 inittab 并执行适当的操作。inittab 中的条目采用以下形式
id:runlevels:action:process
其中 id
是条目的唯一标识符(只是一个名称,对 init 没有实际影响),而 runlevels
是一个(未分隔的)运行级别字符串。如果 init 正要进入的运行级别出现在 runlevels
中,则执行 action
,并在适当的时候执行 process
。某些特殊的 action
会导致 init 忽略 runlevels
并采用特殊的匹配方法。更多解释请参见下一节。
另请参阅 man 5 inittab
和 man 8 init
。
切换运行级别
通过引导加载器
要更改系统启动进入的运行级别,只需将所需的运行级别 n
添加到相应引导加载器的配置文件行中。一个常见的应用是 Xinit#在登录时自动启动 X。要启动到所需的运行级别,请将其编号添加到内核参数中(例如,运行级别 3 为 3
)。
运行级别附加在末尾,以便内核知道从哪个运行级别开始启动。要使用另一个 init 程序(例如 systemd),请添加 init=/usr/lib/systemd/systemd
或类似内容。
启动后
系统启动后,您可以发出 telinit n
命令通知 init 将运行级别更改为 n
。然后 init 读取 inittab 并“比较”运行级别 n 和当前运行级别 - 终止新运行级别中不存在的进程,并执行旧运行级别中不存在的操作。两个运行级别都存在的进程保持不变。终止过程实际上有点复杂;同样,技术细节可以在 init 手册页中找到。
init 不会监视 inittab。您需要显式调用 telinit
才能应用对 inittab 的修改。命令 telinit q
使 init 重新检查 inittab,但不切换运行级别。
inittab
在本节中,我们将检查 inittab 中的常见条目,顺序与 Arch 使用的默认 inittab 中的顺序相同。之后,有一些示例可以帮助您创建自己的 inittab 条目。
telinit q
测试修改后的 /etc/inittab
,否则小的语法错误可能会阻止系统启动。默认运行级别
默认运行级别为 3。如果您希望默认启动进入运行级别 5(传统上用于 X),请取消注释或添加此项
id:5:initdefault:
主启动脚本
这些是主要的 Arch init 脚本。
rc::sysinit:/etc/rc.sysinit rs:S1:wait:/etc/rc.single rm:2345:wait:/etc/rc.multi rh:06:wait:/etc/rc.shutdown
单用户模式启动
有时,由于硬盘或文件系统损坏或损坏、缺少密钥文件等原因,您的内核可能无法完全启动。在这种情况下,您的 init 镜像可能会自动进入单用户模式,该模式仅允许 root 用户登录,并使用 /sbin/sulogin
而不是 /sbin/login
来控制登录过程。您也可以通过在 GRUB、LILO 或 syslinux 配置文件中将字母 S
附加到内核命令行来启动进入单用户模式。如果您希望运行 sulogin 以外的其他程序,请在此处指定。
su:S:wait:/sbin/sulogin -p
Gettys 和登录
这些是运行终端 gettys 的关键条目。大多数默认配置将在 ttys1-6 上运行多个 getty,这会在屏幕上弹出登录提示符。另请参阅 openvt、chvt、stty 和 ioctl。
c1:234:respawn:/sbin/agetty 9600 tty1 xterm-color c5:5:respawn:/sbin/agetty 57600 tty2 xterm-256color
Ctrl+Alt+Del
当按下特殊按键序列 Ctrl+Alt+Del
时,这将决定要执行的操作。
ca::ctrlaltdel:/sbin/shutdown -t3 -r now
X 程序
如果您不害怕调试,您可以弄清楚如何从 inittab 启动各种程序。一种有用的程序类型是在运行级别 5(多用户 X 模式)时启动登录管理器。在以下示例中,您可以看到如何在进入运行级别 5 时启动 SLiM。
x:5:respawn:/usr/bin/slim >/dev/null 2>&1 #x:5:respawn:/usr/bin/xdm -nodaemon -confi /etc/X11/xdm/archlinux/xdm-config
电源感应脚本
Init 可以与您的 UPS 设备通信,并根据 UPS 的状态执行进程。以下是一些示例
pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down" pr:12345:powerokwait:/sbin/shutdown -c "Power Restored; Shutdown Cancelled"
自定义键盘请求
以下行添加了一个自定义函数,用于在按下特殊按键序列时执行。您可以将此特殊按键序列修改为您喜欢的任何内容,类似于 Ctrl+Alt+Del
。
kb::kbrequest:/usr/bin/wall "Keyboard Request -- edit /etc/inittab to customize"
触发 kbrequest
您可以通过向 init 进程(即 PID 1)发送 WINCH 信号(作为 root 用户)来触发特殊按键序列 kbrequest。在本例中,命令
# kill -WINCH 1
导致 wall
写入所有 tty
Broadcast message from root@askapachehost (console) (Wed Oct 27 14:02:26 2010): Keyboard Request -- edit /etc/inittab to customize
编写 rc.d 脚本
Initscripts 使用 rc.d 脚本来控制 守护进程的启动、停止和重启。
指南
- Source
/etc/rc.conf
、/etc/rc.d/functions
和可选的/etc/conf.d/DAEMON_NAME
。 - 参数和其他守护进程选项应放置在
/etc/conf.d/DAEMON_NAME
中。这样做是为了将配置与逻辑分离,并在守护进程脚本之间保持一致的风格。 - 使用
/etc/rc.d/functions
中的函数,而不是复制它们的功能。 - 脚本应至少包含 start、stop 和 restart 参数。
可用函数
/etc/rc.d/functions
提供了一些函数stat_busy "message"
:为打印的消息设置繁忙状态(例如,启动守护进程 [BUSY])stat_done
:设置状态为完成(例如,启动守护进程 [DONE])stat_fail
:设置状态为失败(例如,启动守护进程 [FAILED])get_pid program
:获取程序的 PIDck_pidfile PID-file program
:检查 PID 文件是否仍然对程序有效(例如,ck_pidfile /var/run/daemon.pid daemon || rm -f /var/run/daemon.pid)[add|rm]_daemon program
:添加/移除正在运行的守护进程中的程序(存储在/run/daemons/
中)
完整函数列表更长,大多数可能性(例如,控制非 root 用户是否可以启动守护进程的方式)仍未记录在案,只能从 /etc/rc.d/functions
源代码中学习。另请参阅 man rc.d
。
示例
以下是 crond 的示例。在 /etc/rc.d
中查找更多种类。
配置文件
/etc/conf.d/crond
ARGS="-S -l info"
实际脚本
/etc/rc.d/crond
#!/bin/bash . /etc/rc.conf . /etc/rc.d/functions DAEMON=crond ARGS= [ -r /etc/conf.d/$DAEMON ] && . /etc/conf.d/$DAEMON PID=$(get_pid $DAEMON) case "$1" in start) stat_busy "Starting $DAEMON" [ -z "$PID" ] && $DAEMON $ARGS &>/dev/null if [ $? = 0 ]; then add_daemon $DAEMON stat_done else stat_fail exit 1 fi ;; stop) stat_busy "Stopping $DAEMON" [ -n "$PID" ] && kill $PID &>/dev/null if [ $? = 0 ]; then rm_daemon $DAEMON stat_done else stat_fail exit 1 fi ;; restart) $0 stop sleep 1 $0 start ;; *) echo "usage: $0 {start|stop|restart}" esac
运行级别
来自 init(8)
- 运行级别是系统的一种软件配置,它只允许存在选定的一组进程。init 为每个运行级别生成的进程在
/etc/inittab
文件中定义。
如果您的 Arch 设置出现问题,导致系统启动时您完全无助,您可能需要此功能。
例如,如果您使用一些有缺陷的显示驱动程序,系统可能会在 X 服务器启动时冻结。如果您的启动守护进程列表中有一个显示管理器,您需要在该守护进程启动之前完全控制您的系统。
您该如何做到这一点呢?
你所需要的是所谓的“启动到另一个运行级别”。这基本上决定了系统在启动序列结束后将处于何种状态。通常,你会在多用户模式下完成启动,所有守护进程都已启动(=运行级别 3)。
与 systemd 目标的比较
systemd 目标 | SysV 运行级别 | 注释 |
---|---|---|
runlevel0.target, poweroff.target | 0 | 关闭系统。 |
runlevel1.target, rescue.target | 1, s, single | 单用户模式。 |
runlevel2.target, runlevel4.target, multi-user.target | 2, 4 | 用户自定义/站点特定的运行级别。默认情况下,与 3 相同。 |
runlevel3.target, multi-user.target | 3 | 多用户,非图形界面。用户通常可以通过多个控制台或网络登录。 |
runlevel5.target, graphical.target | 5 | 多用户,图形界面。通常具有运行级别 3 的所有服务以及图形登录界面。 |
runlevel6.target, reboot.target | 6 | 重启 |
emergency.target | emergency | 紧急 Shell |
initscripts 运行级别列表
还有哪些可能的运行级别?
- 1:单用户(维护模式):当您遇到问题时,可以使用此模式。
- 3:多用户:正常模式
- 5:带 X11 的多用户:与 3 相同,但默认情况下在虚拟终端 8 中加载了 X11
- 0:停止
- 6:重启
- 2, 4:未使用
查看 /etc/inittab
以了解其工作原理。
运行级别调用
您需要在内核命令行中指定要进入的运行级别。您只需将所需运行级别的数字作为选项传递到该命令行即可,如果您遇到问题并想使用单用户模式,它可能看起来像这样(这里只有最后一个数字是重要的)
kernel /vmlinuz-linux ... root=/dev/sda2 ro 1
是的,如果您无法启动,则需要在启动时在启动管理器中将运行级别编号附加到内核命令行。
添加运行级别
第一种方法
在本页中,将使用 4 作为示例,因为它在 Arch 中默认未使用。要创建另一个运行级别
# cd /etc # cp rc.multi rc.multi4 # sed -i "s/DAEMONS/DAEMONS4/g" /etc/rc.multi4
执行 sed 将更改 /etc/rc.multi4
,使其查看将在接下来的几个步骤中定义的新 DAEMONS 数组。
接下来,我们将通过更改此行将新的 /etc/rc.multi4
脚本添加到 /etc/inittab
中
rm:2345:wait:/etc/rc.multi
改为
rm:235:wait:/etc/rc.multi ra:4:wait:/etc/rc.multi4
您还可以在 /etc/inittab
中添加新行以执行其他脚本或程序,以执行您想做的任何事情。
示例:以单用户身份登录 X 以用于特殊目的
xa:4:respawn:/bin/su - $USER -c "/usr/bin/startx"
下一步是在您的 /etc/rc.conf
文件中添加一个新的 DAEMONS 数组,将其命名为 DAEMONS4=(...)
,并在该数组中填充您希望为新运行级别运行的任何守护进程。
/etc/rc.conf
建议在要禁用的守护进程前面加上 !
。在默认的 /etc/rc.multi
中,以 !
开头的任何内容都会被跳过。这样做的一个缺点是,如果您使用上述方法为新的运行级别定义一组不同的守护进程(即,想要停止一些,保持其他运行,和/或启动新的),则在切换到或从新的运行级别切换时,任何以 !
开头的守护进程都不会被停止。以下 /etc/rc.multi
更改了此行为。
示例
/etc/rc.multi
#!/bin/bash # # /etc/rc.multi # . /etc/rc.conf . /etc/rc.d/functions run_hook multi_start # Load sysctl variables if sysctl.conf is present [ -r /etc/sysctl.conf ] && /sbin/sysctl -q -p &>/dev/null # Start daemons # _remember to change DAEMONS in next line for the file /etc/rc.multi4 for daemon in "${DAEMONS[@]}"; do if [ "$daemon" = "${daemon#!}" ]; then # check to see if daemon is running. ck_daemon $daemon if [ $? -eq 1 ]; then # daemon is running, skip it. status_started else # daemon is not running, start it. if [ "$daemon" = "${daemon#@}" ]; then start_daemon $daemon else start_daemon_bkgd ${daemon:1} fi fi else # check previous runlevel. if it's N, then we've just booted # and do not need to stop any daemons. otherwise, stop daemons # when runlevel changes as requested in DAEMONS array. if [ `/sbin/runlevel | cut -d ' ' -f 1` != "N" ] ; then ck_daemon ${daemon:1} if [ $? -eq 1 ] ; then # daemon is running, let's stop it. stop_daemon ${daemon:1} fi fi fi done if [ -x /etc/rc.local ]; then /etc/rc.local fi run_hook multi_end # vim: set ts=2 noet:
这是它的作用
DAEMONS=(syslog-ng network netfs sshd alsa !jack !gpm) # runlevel 3 DAEMONS4=(syslog-ng network netfs !sshd alsa jack gpm) # runlevel 4
在运行级别 3 中,jack 和 gpm 被禁用,而在运行级别 4 中,不需要 sshd,但需要 jack 和 gpm。上面的 /etc/rc.multi
脚本将扫描 daemons 数组并检查
- 如果守护进程正在运行(没有
!
)。如果是,则跳过它。如果不是,则启动它。 - 如果守护进程被禁用 (
!
),则停止它。(这在启动时被跳过) - 它仍然支持在后台启动守护进程 (
@
)
在上面的示例中,当从运行级别 3 切换到运行级别 4 时,syslog-ng、network、netfs 和 alsa 被检查并发现正在运行,因此它们将被跳过。sshd 将被禁用,然后 jack 和 gpm 将被启动。当从运行级别 4 切换到运行级别 3 时,syslog-ng、network、netfs 和 alsa 仍在运行,因此将再次被跳过,但 jack 和 gpm 将被停止,而 sshd 将再次被启动。
总结
- 复制
/etc/rc.multi
到/etc/rc.multi4
- 将 DAEMONS4 添加到
/etc/rc.conf
的末尾,并在其中添加守护进程 - 确保通过将 DAEMONS 更改为 DAEMONS4 来更改
/etc/rc.multi4
- 编辑
/etc/inittab
以添加运行级别并采取适当的操作
如果您使用上述 /etc/rc.multi
,正确的操作是使其既是您的主 /etc/rc.multi
又是您的新 /etc/rc.multi4
,以确保所有守护进程都按您的意愿处理。拥有两个不同版本的 /etc/rc.multi
不会破坏您的系统。
虽然您的新运行级别设置不应被任何系统更新覆盖,但在发生意外情况时,手头备有备份总是很方便的。
另一种方法,无需添加任何符号链接
通过对 /etc/rc.multi
进行简单的修改,只需在 /etc/rc.conf
中添加新的 DAEMONS 行即可轻松添加运行级别。
这是补丁
--- rc.multi 2008-06-22 23:58:29.000000000 +0200 +++ rc.multi.new 2008-06-23 00:14:05.000000000 +0200 @@ -11,8 +11,25 @@ # Load sysctl variables if sysctl.conf is present [ -r /etc/sysctl.conf ] && /sbin/sysctl -q -p &>/dev/null +# Load the appropriate DAEMONS array according to runlevel specified in the kernel boot cmdline +RUNLEVEL="" +FINAL_DAEMONS=() + +for param in `cat /proc/cmdline`; do + param_rl=`echo $param | grep ^runlevel` + if [ ! "$param_rl" = "" ]; then + RUNLEVEL=`echo $param_rl | sed -r -e "s#runlevel=(.+)#\1#"` + fi +done; + +if [ "${RUNLEVEL}" = "" ]; then + eval FINAL_DAEMONS=(${DAEMONS[@]}) +else + eval FINAL_DAEMONS=(\${DAEMONS_${RUNLEVEL}[@]}) + if [ "${#FINAL_DAEMONS[@]}" = "0" ]; then + eval FINAL_DAEMONS=(${DAEMONS[@]}) + fi +fi + # Start daemons -for daemon in "${DAEMONS[@]}"; do +for daemon in "${FINAL_DAEMONS[@]}"; do if [ "$daemon" = "${daemon#!}" ]; then if [ "$daemon" = "${daemon#@}" ]; then /etc/rc.d/$daemon start
现在,要添加运行级别,请在 /etc/rc.conf
中添加一个新的数组(在本例中我将其命名为 FOO
)
DAEMONS_FOO=( ...whatever... )
要使用此运行级别运行系统,只需在 LILO 或 GRUB 的启动参数中添加 runlevel=FOO
。
其他发行版
运行级别存在于所有 Linux 发行版中,虽然运行级别 1 通常是单用户“紧急模式”,0 意味着停止,6 意味着重启,但其他运行级别的含义因发行版而异。