当启动计算机时,控制台上滚动的大量信息显示许多初始化和配置工作正在自动执行。有时候你可能稍稍的改变这一阶段的操作,就要求你需要很好的理解他们。这正是本章节的目的所在。
On systems with a BIOS, first, the BIOS takes control of the computer, initializes the controllers and hardware, detects the disks, and bridges everything together. Then it looks up the Master Boot Record (MBR) of the first disk in the boot order and loads the code stored there (first stage). This code then launches the second stage and finally executes the bootloader.
In contrast to the BIOS, UEFI is more sophisticated, it knows filesystems and can read the partition tables. The interface searches the system storage for a partition labeled with a specific globally unique identifier (
GUID) that marks it as the
EFI System Partition (
ESP), where the bootloaders, boot managers, UEFI shell, etc., are located, and launches the desired bootloader. If Secure Boot is enabled, the boot process will verify authenticity of the EFI binaries there by signature (thus
grub-efi-arch-signed is required in this case). The UEFI specification also defines support for booting in legacy BIOS mode. This is called the
Compatibility Support Module (
CSM). If CSM is enabled, it will attempt to boot from a drive's MBR. However, many new systems do no longer support the CSM mode.
In both cases then the actual bootloader takes over, finds either a chained bootloader or the kernel on the disk, loads, and executes it. The kernel is then initialized, and starts to search for and mount the partition containing the root filesystem, and finally executes the first program — init
. Frequently, this “root partition” and this init
are, in fact, located in a virtual filesystem that only exists in RAM (hence its name, “initramfs”, formerly called “initrd” for “initialization RAM disk”). This filesystem is loaded in memory by the bootloader, often from a file on a hard drive or from the network. It contains the bare minimum required by the kernel to load the “true” root filesystem: this may be driver modules for the hard drive, or other devices without which the system cannot boot, or, more frequently, initialization scripts and modules for assembling RAID arrays, opening encrypted partitions, activating LVM volumes, etc. Once the root partition is mounted, the initramfs hands over control to the real init, and the machine goes back to the standard boot process.
“真正的启动器”当前是由systemd 提供的,本章节讲述该启动系统。
Systemd 运行多个进程,设置系统:键盘、驱动程序、文件系统、网络、服务等。同时全面查看系统,以及必要的配件。每个配件都视为一个 “单元文件” (有时为多个);通用的语法源自于常用的 “*.ini files“ 语法,包括配对的 key = value
列在 [section]
标头内。单元文件保存在 /lib/systemd/system/
与 /etc/systemd/system/
内;以多种方式呈现,目前专注在 “services” 与 “targets”。
A systemd “.service
file” describes a process managed by systemd. It contains roughly the same information as old-style init-scripts, but expressed in a declaratory (and much more concise) way. Systemd handles the bulk of the repetitive tasks (starting and stopping the process, checking its status, logging, dropping privileges, and so on), and the service file only needs to fill in the specifics of the process. For instance, here is the service file for SSH:
[Unit]
Description=OpenBSD Secure Shell server
Documentation=man:sshd(8) man:sshd_config(5)
After=network.target auditd.service
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run
[Service]
EnvironmentFile=-/etc/default/ssh
ExecStartPre=/usr/sbin/sshd -t
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
ExecReload=/usr/sbin/sshd -t
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartPreventExitStatus=255
Type=notify
RuntimeDirectory=sshd
RuntimeDirectoryMode=0755
[Install]
WantedBy=multi-user.target
Alias=sshd.service
The [Unit]
section contains generic information about the service, like its description and manual page resources, as well as relations (dependency and order) to other services. The [Service]
part contains the declarations related to the service execution (starting, stopping, killing, restarting), directories and configuration file(s) used. The last section, [Install]
, again carries generic information into which targets to install the service and, in this case, the alias that can be used instead of the service name. As you can see, there is very little code in there, only declarations. Systemd takes care of displaying progress reports, keeping track of the processes, and even restarting them when needed. The syntax of these files is fully described in several manual pages (e.g. systemd.service(5), systemd.unit(5), systemd.exec(5), etc.).
A systemd “.target
file” describes a state of the system where a set of services are known to be operational. It can be thought of as an equivalent of the old-style runlevel. One of the pre-defined targets is local-fs.target
; when it is reached, the rest of the system can assume that all local filesystems are mounted and accessible. Other targets include network-online.target
and sound.target
(for a full list of special targets see systemd.special(7)). The dependencies of a target can be listed either within the target file (in the Requires=
line), or using a symbolic link to a service file in the /lib/systemd/system/targetname.target.wants/
directory. For instance, /etc/systemd/system/printer.target.wants/
contains a link to /lib/systemd/system/cups.service
; systemd will therefore ensure CUPS is running in order to reach 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
#
检查服务的状态 (失败) 后,再检查日志档;它们会指出配置的错误。编辑配置档并修正错误后,重启服务,确认运行中。
The System V init system (which we'll call init for brevity) executes several processes, following instructions from the /etc/inittab
file. The first program that is executed (which corresponds to the sysinit step) is /etc/init.d/rcS
, a script that executes all of the programs in the /etc/rcS.d/
directory.
其中,你会发现相继的程序会负责:
到了这个地步,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
目录下的脚本)它们来适应不同的需求。
All the scripts contained in the various /etc/rcX.d
directories are really only symbolic links — created upon package installation by the update-rc.d
program — pointing to the actual scripts which are stored in /etc/init.d/
. The administrator can fine tune the services available in each runlevel by re-running update-rc.d
with adjusted parameters. The update-rc.d(1) manual page describes the syntax in detail. Please note that removing all symbolic links (with the remove
parameter) is not a good method to disable a service. Instead you should simply configure it to not start in the desired runlevel (while preserving the corresponding calls to stop it in the event that the service runs in the previous runlevel). Since update-rc.d
has a somewhat convoluted interface, you may prefer using rcconf
(from the rcconf package) which provides a more user-friendly interface.
最后,init
启动各种虚拟控制台的控制程序(getty
)。显示提示符,等待输入用户名,然后执行login user
发起会话。