当启动计算机时,控制台上滚动的大量信息显示许多初始化和配置工作自动正在执行。有时候你可能稍稍的改变这一阶段的操作,就要求你需要很好的理解他们。这正是本章节的目的所在。
“真正的启动器”当前是由systemd 提供的,本章节讲述该启动系统。
Systemd 运行多个进程,设置系统:键盘、驱动程序、文件系统、网络、服务等。同时全面查看系统,以及必要的配件。每个配件都视为一个 “单元文件” (有时为多个);通用的语法源自于常用的 “*.ini files“ 语法,包括配对的 key = value
列在 [section]
标头内。单元文件保存在 /lib/systemd/system/
与 /etc/systemd/system/
内;以多种风貌呈现,目前专注在 “服务” 与 “目标”。
systemd “服务文件” 描述被 systemd 管理的进程。包括与旧型的 init-scripts 相同的数据,但以声明 (同时较为简洁) 的方式表述。Systemd 处理大量重复的工作 (启动与终止进程、检查其状态、日注记录、去除特权等),以及只供特定进程使用的服务文件。例如,以下是 SSH 用到的服务档:
[Unit]
Description=OpenBSD Secure Shell server
After=network.target auditd.service
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run
[Service]
EnvironmentFile=-/etc/default/ssh
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
[Install]
WantedBy=multi-user.target
Alias=sshd.service
如上文所示,代码极少,只有声明。Systemd 管理显示进度报表、追踪进程、以及必要的重启。
systemd 的 “目标文件” 描述系统的现状,包括可操作的服务。不妨视为相当于旧型的运行阶段作业。其中一个目标是 local-fs.target
;进入之后,系统的其他部分假设所有的本地文件系统均己挂载并可近用。其他的目标包括 network-online.target
与 sound.target
。目标的相依性可以列在目标文件内 (于 Requires=
列) 或使用符号链接至在 /lib/systemd/system/targetname.target.wants/
文件夹内的服务文件。例如,/etc/systemd/system/printer.target.wants/
包括一个链接至 /lib/systemd/system/cups.service
;systemd 将确保 CUPS 已运行至 printer.target
。
单元文件是声明性的而不是脚本或程序,不能直接运行,只能被 systemd 解译;因些有些工具允许管理者与 systemd 交互且控制系统的状态与其组件。
第一种这类工具是 systemctl
。未使用参数运行时,它列出 systemd 已知的所有单元档 (除了已经停用的),及其现况。systemctl status
则以更佳的角度查看服务,以及相关的进程。若提供服务的名称 (如 systemctl status ntp.service
),则送回更多详细的数据,以及与该服务有关的最后几个日志档 (还有更多的)。
运行 systemctl start servicename.service
就能以人工方式启动服务。同样的,运行 systemctl stop servicename.service
就能停止已完成的服务;其他的次命令包括 reload
与 restart
。
以 systemctl enable servicename.service
(或 disable
) 控制启动服务 (即开机后自动启动)。is-enabled
可以检查服务的状态。
systemd 重要的功能之一是包括登录的组件 journald
。做为补充 syslogd
之类传统登录系统的组件,但加入额外的功能包括在服务与其产生消息间的正式链接,以及补捉由初始过程产生的错误消息。在 journalctl
命令的协助下,稍后可显示该等消息。不需任何参数,它溢出系统启动后发生的所有日志消息;不过很少用到它。多数时间,把它做为服务的辨识器:
#
journalctl -u ssh.service
-- Logs begin at Tue 2015-03-31 10:08:49 CEST, end at Tue 2015-03-31 17:06:02 CEST. --
Mar 31 10:08:55 mirtuel sshd[430]: Server listening on 0.0.0.0 port 22.
Mar 31 10:08:55 mirtuel sshd[430]: Server listening on :: port 22.
Mar 31 10:09:00 mirtuel sshd[430]: Received SIGHUP; restarting.
Mar 31 10:09:00 mirtuel sshd[430]: Server listening on 0.0.0.0 port 22.
Mar 31 10:09:00 mirtuel sshd[430]: Server listening on :: port 22.
Mar 31 10:09:32 mirtuel sshd[1151]: Accepted password for roland from 192.168.1.129 port 53394 ssh2
Mar 31 10:09:32 mirtuel sshd[1151]: pam_unix(sshd:session): session opened for user roland by (uid=0)
另个有用的命令行旗标是 -f
,用于指示 journalctl
继续显示溢出的添加消息 (大部分是在 tail -f file
之内)。
若服务状况不如预共,第一个步骤是以 systemctl status
检查该服务是否真的已启动;若没有,则第一个命令给的消息就不足以诊断问题之所在,检查 journald 产生的日志档。例如,假设 SSH 服务器未启动时:
#
systemctl status ssh.service
● ssh.service - OpenBSD Secure Shell server
Loaded: loaded (/lib/systemd/system/ssh.service; enabled)
Active: failed (Result: start-limit) since Tue 2015-03-31 17:30:36 CEST; 1s ago
Process: 1023 ExecReload=/bin/kill -HUP $MAINPID (code=exited, status=0/SUCCESS)
Process: 1188 ExecStart=/usr/sbin/sshd -D $SSHD_OPTS (code=exited, status=255)
Main PID: 1188 (code=exited, status=255)
Mar 31 17:30:36 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
Mar 31 17:30:36 mirtuel systemd[1]: Unit ssh.service entered failed state.
Mar 31 17:30:36 mirtuel systemd[1]: ssh.service start request repeated too quickly, refusing to start.
Mar 31 17:30:36 mirtuel systemd[1]: Failed to start OpenBSD Secure Shell server.
Mar 31 17:30:36 mirtuel systemd[1]: Unit ssh.service entered failed state.
#
journalctl -u ssh.service
-- Logs begin at Tue 2015-03-31 17:29:27 CEST, end at Tue 2015-03-31 17:30:36 CEST. --
Mar 31 17:29:27 mirtuel sshd[424]: Server listening on 0.0.0.0 port 22.
Mar 31 17:29:27 mirtuel sshd[424]: Server listening on :: port 22.
Mar 31 17:29:29 mirtuel sshd[424]: Received SIGHUP; restarting.
Mar 31 17:29:29 mirtuel sshd[424]: Server listening on 0.0.0.0 port 22.
Mar 31 17:29:29 mirtuel sshd[424]: Server listening on :: port 22.
Mar 31 17:30:10 mirtuel sshd[1147]: Accepted password for roland from 192.168.1.129 port 38742 ssh2
Mar 31 17:30:10 mirtuel sshd[1147]: pam_unix(sshd:session): session opened for user roland by (uid=0)
Mar 31 17:30:35 mirtuel sshd[1180]: /etc/ssh/sshd_config line 28: unsupported option "yess".
Mar 31 17:30:35 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
Mar 31 17:30:35 mirtuel systemd[1]: Unit ssh.service entered failed state.
Mar 31 17:30:35 mirtuel sshd[1182]: /etc/ssh/sshd_config line 28: unsupported option "yess".
Mar 31 17:30:35 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
Mar 31 17:30:35 mirtuel systemd[1]: Unit ssh.service entered failed state.
Mar 31 17:30:35 mirtuel sshd[1184]: /etc/ssh/sshd_config line 28: unsupported option "yess".
Mar 31 17:30:35 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
Mar 31 17:30:35 mirtuel systemd[1]: Unit ssh.service entered failed state.
Mar 31 17:30:36 mirtuel sshd[1186]: /etc/ssh/sshd_config line 28: unsupported option "yess".
Mar 31 17:30:36 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
Mar 31 17:30:36 mirtuel systemd[1]: Unit ssh.service entered failed state.
Mar 31 17:30:36 mirtuel sshd[1188]: /etc/ssh/sshd_config line 28: unsupported option "yess".
Mar 31 17:30:36 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
Mar 31 17:30:36 mirtuel systemd[1]: Unit ssh.service entered failed state.
Mar 31 17:30:36 mirtuel systemd[1]: ssh.service start request repeated too quickly, refusing to start.
Mar 31 17:30:36 mirtuel systemd[1]: Failed to start OpenBSD Secure Shell server.
Mar 31 17:30:36 mirtuel systemd[1]: Unit ssh.service entered failed state.
#
vi /etc/ssh/sshd_config
#
systemctl start ssh.service
#
systemctl status ssh.service
● ssh.service - OpenBSD Secure Shell server
Loaded: loaded (/lib/systemd/system/ssh.service; enabled)
Active: active (running) since Tue 2015-03-31 17:31:09 CEST; 2s ago
Process: 1023 ExecReload=/bin/kill -HUP $MAINPID (code=exited, status=0/SUCCESS)
Main PID: 1222 (sshd)
CGroup: /system.slice/ssh.service
└─1222 /usr/sbin/sshd -D
#
检查服务的状态 (失败) 后,再检查日志档;它们会指出配置的错误。编辑配置档并修正错误后,重启服务,确认运行中。
System V 初始系统 (简称初始) 运行若干进程,根据
/etc/inittab
文件的指令做事。第一个运行的程序 (映射于
sysinit 步骤) 是
/etc/init.d/rcS
,一个运行在
/etc/rcS.d/
文件夹内所有程序的脚本。
其中,你会发现相继的程序会负责:
到了这个地步,init
接手并启动运行阶段缺省的程序 (通常是运行阶段 2)。它运行 /etc/init.d/rc 2
,一个启动列在 /etc/rc2.d/
之内的所有服务并命名为 “S” 字母开头。接着的两位数,曾经做为服务启动的顺序,不过现在的缺省启动系统使用 insserv
,根据脚本的相依性自动决定其先后顺序。每个启动脚本声明的情况必须符合启动或停止服务 (例如,必须在另个服务之前或之后启动);init
再依此情况启动它们。不再考虑静态的脚本编号 (但仍需按相依性使用 “S” 及两个数字与实际的脚本名称)。通常,基本的服务 (诸如以 rsyslog
登录,或以 portmap
指定端口口) 先列出来,然后才是标准服务与图形接口 (gdm3
)。
这种以依赖为基础的启动系统使自动排序成为可能,这样的排序如果要手工完成则显得冗长乏味。由于调度根据明确给出的参数进行,这样就避免了人为错误。另一个好处是,如果两个服务彼此独立,则可以并行启动,进而加速启动过程。
init
分几个运行等级,它可以通过telinit new-level
命令,从一个等级切换到另一个等级。马上就会在新等级下重新执行init
executes /etc/init.d/rc
。这个脚本会启动漏掉的服务并中止不再需要的服务。为了做到这一点,它读取/etc/rcX.d
文件的内容(此处 X 代表新的运行等级)。以“S"(Start的首字母)开头的服务脚本要启动;以“K"(Kill的首字母)开头的服务要停止。脚本不会启动在之前运行等级已经生效的服务。
缺省,Debian 的 System V init 使用四个不同的运行阶层:
等级0仅作电脑关机时的临时应用。这样,它只包含许多“K”脚本。
等级1,也被称为单用户模式,对应于降级的系统模式;它仅包含基本服务,用于维护,此时不需要与一般用户交互。
等级2用于正常运行,包含网络服务,图形界面,用户登陆,等等。
等级6和等级0类似,不同在于它用于系统重启之前的关机。
也存在其他等级,从3到5。默认情况,他们配置为和等级2相同,但是管理员可以修改(通过添加和删除对应/etc/rcX.d
目录下的脚本)它们来适应不同的需求。
所有包含在/etc/rcX.d
目录下的脚本都只是符号联接-有update-rc.d
程序在安装时创建-指向存储在/etc/init.d/
中的实际脚本。管理员可以使用调整后的参数重新运行 update-rc.d
来微调每个运行等级的服务。update-rc.d(1)手册详细介绍了语法。请注意,使用remove
参数移除所有的符号连接不是停用服务的好办法。取而代之的方法是,你可以在特定的运行等级将其配置为不启动(而保留先前等级对应事件的停止调用)。由于update-rc.d
的接口有些绕,可以考虑使用rcconf
(出自rcconf 软件包),它提供了更加友好的界面。
最后,init
启动各种虚拟控制台的控制程序(getty
)。显示提示符,等待输入用户名,然后执行login user
发起会话。