Product SiteDocumentation Site

第 9 章 Unix 服务

9.1. 系统启动
9.1.1. Systemd 启动系统
9.1.2. The System V init system
9.2. 远程登录
9.2.1. 安全远程登录:SSH
9.2.2. 使用远程图形桌面
9.3. 管理权限
9.4. 管理员介面
9.4.1. Administrating on a Web Interface: webmin
9.4.2. 用于管理配置的软件包: debconf
9.5. syslog 系统事件
9.5.1. 原则和机制
9.5.2. 配置文件
9.6. The inetd Super-Server
9.7. 使用 cronatd运行计划任务
9.7.1. crontab 文件的格式
9.7.2. 使用 at 命令
9.8. 调度异步任务:anacron
9.9. 配额
9.10. 备份
9.10.1. 使用 rsync备份
9.10.2. 不使用备份恢复系统
9.11. 热插拔: 热插拔
9.11.1. 介绍
9.11.2. 命名问题
9.11.3. udev 如何工作
9.11.4. 一个具体例子
9.12. Power Management: Advanced Configuration and Power Interface (ACPI)
本章涵盖一些Unix系统共通的服务。管理员应当熟悉他们。

9.1. 系统启动

当启动计算机时,控制台上滚动的大量信息显示许多初始化和配置工作自动正在执行。有时候你可能稍稍的改变这一阶段的操作,就要求你需要很好的理解他们。这正是本章节的目的所在。
首先,BIOS 控制电脑,探测磁盘,加载Master Boot Record,并执行启动加载器。启动程序接手后,找到磁盘上的内核,加载并执行。然后,内核被初始化,并开始寻找和挂载包含根文件系统的分区,最后执行第一个程序-init。根分区和启动程序init常常驻留在仅存在于 RAM 中的虚拟文件系统(正如它的名字,“initramfs”,一般称之为“initrd”初始内存磁盘)。启动加载器将文件系统加载到内存中,文件通常位于硬盘或者源于网络。它包含了内核需要的最少裸信息,以便用来加载“真正”的根文件系统:可能是硬盘上的驱动模块,或者其他系统启动必须的设备,或者更常见的是初始化脚本和模块以组建 RAID 阵列,打开加密分区,激活 LVM ,等等。一旦根分区挂载,initramfs 就会把控制权交到真正的启动程序,机器则回到标准的启动过程。
运行 sytemd 的 Linux 计算机的启动顺序

图 9.1. 运行 sytemd 的 Linux 计算机的启动顺序

9.1.1. Systemd 启动系统

“真正的启动器”当前是由systemd 提供的,本章节讲述该启动系统。
Systemd executes several processes, in charge of setting up the system: keyboard, drivers, filesystems, network, services. It does this while keeping a global view of the system as a whole, and the requirements of the components. Each component is described by a “unit file” (sometimes more); the general syntax is derived from the widely-used “*.ini files“ syntax, with key = value pairs grouped between [section] headers. Unit files are stored under /lib/systemd/system/ and /etc/systemd/system/; they come in several flavours, but we will focus on “services” and “targets” here.
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
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
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.
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 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. 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.
Since unit files are declarative rather than scripts or programs, they cannot be run directly, and they are only interpreted by systemd; several utilities therefore allow the administrator to interact with systemd and control the state of the system and of each component.
The first such utility is systemctl. When run without any arguments, it lists all the unit files known to systemd (except those that have been disabled), as well as their status. systemctl status gives a better view of the services, as well as the related processes. If given the name of a service (as in systemctl status ntp.service), it returns even more details, as well as the last few log lines related to the service (more on that later).
Starting a service by hand is a simple matter of running systemctl start servicename.service. As one can guess, stopping the service is done with systemctl stop servicename.service; other subcommands include reload and restart.
To control whether a service is active (i.e. whether it will get started automatically on boot), use systemctl enable servicename.service (or disable). is-enabled allows checking the status of the service.
An interesting feature of systemd is that it includes a logging component named journald. It comes as a complement to more traditional logging systems such as syslogd, but it adds interesting features such as a formal link between a service and the messages it generates, and the ability to capture error messages generated by its initialisation sequence. The messages can be displayed later on, with a little help from the journalctl command. Without any arguments, it simply spews all log messages that occurred since system boot; it will rarely be used in such a manner. Most of the time, it will be used with a service identifier:
# 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)
Another useful command-line flag is -f, which instructs journalctl to keep displaying new messages as they are emitted (much in the manner of tail -f file).
If a service doesn't seem to be working as expected, the first step to solve the problem is to check that the service is actually running with systemctl status; if it is not, and the messages given by the first command are not enough to diagnose the problem, check the logs gathered by journald about that service. For instance, assume the SSH server doesn't work:
# 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
# 
After checking the status of the service (failed), we went on to check the logs; they indicate an error in the configuration file. After editing the configuration file and fixing the error, we restart the service, then verify that it is indeed running.

9.1.2. The System V init system

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.
其中,你会发现相继的程序会负责:
  • 配置控制台键盘;
  • 加载驱动:当探测到硬件,大部分内核模块通过内核自身加载;然后,自动加载 /etc/modules 中列出的模块;
  • 检查文件系统的完整性;
  • 挂载本地分区;
  • 配置网络;
  • 挂载网络文件系统(NFS)。
After this stage, init takes over and starts the programs enabled in the default runlevel (which is usually runlevel 2). It executes /etc/init.d/rc 2, a script that starts all services which are listed in /etc/rc2.d/ and whose names start with the “S” letter. The two-figures number that follows had historically been used to define the order in which services had to be started, but nowadays the default boot system uses insserv, which schedules everything automatically based on the scripts' dependencies. Each boot script thus declares the conditions that must be met to start or stop the service (for example, if it must start before or after another service); init then launches them in the order that meets these conditions. The static numbering of scripts is therefore no longer taken into consideration (but they must always have a name beginning with “S” followed by two digits and the actual name of the script used for the dependencies). Generally, base services (such as logging with rsyslog, or port assignment with portmap) are started first, followed by standard services and the graphical interface (gdm3).
这种以依赖为基础的启动系统使自动排序成为可能,这样的排序如果要手工完成则显得冗长乏味。由于调度根据明确给出的参数进行,这样就避免了人为错误。另一个好处是,如果两个服务彼此独立,则可以并行启动,进而加速启动过程。
init分几个运行等级,它可以通过telinit new-level 命令,从一个等级切换到另一个等级。马上就会在新等级下重新执行init executes /etc/init.d/rc。这个脚本会启动漏掉的服务并中止不再需要的服务。为了做到这一点,它读取/etc/rcX.d 文件的内容(此处 X 代表新的运行等级)。以“S"(Start的首字母)开头的服务脚本要启动;以“K"(Kill的首字母)开头的服务要停止。脚本不会启动在之前运行等级已经生效的服务。
By default, System V init in Debian uses four different runlevels:
  • 等级0仅作电脑关机时的临时应用。这样,它只包含许多“K”脚本。
  • 等级1,也被称为单用户模式,对应于降级的系统模式;它仅包含基本服务,用于维护,此时不需要与一般用户交互。
  • 等级2用于正常运行,包含网络服务,图形界面,用户登陆,等等。
  • 等级6和等级0类似,不同在于它用于系统重启之前的关机。
也存在其他等级,从3到5。默认情况,他们配置为和等级2相同,但是管理员可以修改(通过添加和删除对应/etc/rcX.d目录下的脚本)它们来适应不同的需求。
Boot sequence of a computer running Linux with System V init

图 9.2. Boot sequence of a computer running Linux with System V init

所有包含在/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发起会话。