SysVinit

来自 ArchWiki
(重定向自 Inittab

此文章或章节已过时。

原因: 需要对照 initscripts-forkAUR 进行检查(在 Talk:SysVinit 中讨论)
警告: Arch Linux 仅官方支持 systemd[1] 当使用 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 inittabman 8 init

切换运行级别

通过引导加载器

要更改系统启动进入的运行级别,只需将所需的运行级别 n 添加到相应引导加载器的配置文件行中。一个常见的应用是 Xinit#在登录时自动启动 X。要启动到所需的运行级别,请将其编号添加到内核参数中(例如,运行级别 3 为 3)。

运行级别附加在末尾,以便内核知道从哪个运行级别开始启动。要使用另一个 init 程序(例如 systemd),请添加 init=/usr/lib/systemd/systemd 或类似内容。

注意: 如果使用 sysvinit 以外的 init,则运行级别参数可能会被忽略。

启动后

系统启动后,您可以发出 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 来控制登录过程。您也可以通过在 GRUBLILOsyslinux 配置文件中将字母 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:获取程序的 PID
    • ck_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

运行级别

此文章或章节需要语言、wiki 语法或风格改进。请参阅 Help:Style 以获取参考。

原因: 与其他文章部分重复,写作风格像博客文章。(在 Talk:SysVinit 中讨论)
注意: 默认使用 systemd,它使用 targets(参见 systemd.target(5))而不是运行级别。

来自 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 数组并检查

  1. 如果守护进程正在运行(没有 !)。如果是,则跳过它。如果不是,则启动它。
  2. 如果守护进程被禁用 (!),则停止它。(这在启动时被跳过)
  3. 它仍然支持在后台启动守护进程 (@)

在上面的示例中,当从运行级别 3 切换到运行级别 4 时,syslog-ng、network、netfs 和 alsa 被检查并发现正在运行,因此它们将被跳过。sshd 将被禁用,然后 jack 和 gpm 将被启动。当从运行级别 4 切换到运行级别 3 时,syslog-ng、network、netfs 和 alsa 仍在运行,因此将再次被跳过,但 jack 和 gpm 将被停止,而 sshd 将再次被启动。

总结

  1. 复制 /etc/rc.multi/etc/rc.multi4
  2. 将 DAEMONS4 添加到 /etc/rc.conf 的末尾,并在其中添加守护进程
  3. 确保通过将 DAEMONS 更改为 DAEMONS4 来更改 /etc/rc.multi4
  4. 编辑 /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... )

要使用此运行级别运行系统,只需在 LILOGRUB 的启动参数中添加 runlevel=FOO

其他发行版

运行级别存在于所有 Linux 发行版中,虽然运行级别 1 通常是单用户“紧急模式”,0 意味着停止,6 意味着重启,但其他运行级别的含义因发行版而异。

另请参阅