SysVinit
在基于 SysVinit 的系统中,init 是 Linux 内核加载后执行的第一个进程。内核使用的默认 init 程序是由 systemd-sysvcompat (默认安装,参见 systemd) 或 sysvinitAUR 提供的 /sbin/init
。在本文中,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#Configuration。
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 代表 halt,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#Autostart X at login。要启动到所需的运行级别,请将其编号添加到 内核参数 中(例如,运行级别 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
在本节中,我们将按照 Arch 使用的默认 inittab 中出现的顺序检查 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 上运行多个 gettys,这会在您的屏幕上弹出登录提示符。另请参阅 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 脚本来控制 守护进程 的启动、停止和重启。
指南
- 加载
/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 状态(例如,启动守护进程 [BUSY])stat_done
:设置 done 状态(例如,启动守护进程 [DONE])stat_fail
:设置 failed 状态(例如,启动守护进程 [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 targets 的比较
systemd Target | 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 | 紧急 | 紧急 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"
下一步是将新的 DAEMONS 数组添加到您的 /etc/rc.conf
文件中,将其命名为 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 表示重启,但其他运行级别的含义因发行版而异。