Product SiteDocumentation Site

第 9 章 Unix 服务

9.1. 系统启动
9.1.1. Systemd 启动系统
9.1.2. System V 初始系统
9.2. 远程登录
9.2.1. 安全远程登录:SSH
9.2.2. 使用远程图形桌面
9.3. 管理权限
9.4. 管理员界面
9.4.1. 管理网页接口:webmin
9.4.2. 用于管理配置的软件包: debconf
9.5. syslog 系统事件
9.5.1. 原则与机制
9.5.2. 配置文件
9.6. The inetd 超级服务器
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. 电源管理:高端配置与电源接口 (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 flavors, but we will focus on “services” and “targets” here.
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.targetsound.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 就能停止已完成的服务;其他的次命令包括 reloadrestart
systemctl enable servicename.service (或 disable) 控制启动服务 (即开机后自动启动)。is-enabled 可以检查服务的状态。
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 initialization 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)
另个有用的命令行旗标是 -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
# 
检查服务的状态 (失败) 后,再检查日志档;它们会指出配置的错误。编辑配置档并修正错误后,重启服务,确认运行中。

9.1.2. System V 初始系统

System V 初始系统 (简称初始) 运行若干进程,根据 /etc/inittab 文件的指令做事。第一个运行的程序 (映射于 sysinit 步骤) 是 /etc/init.d/rcS,一个运行在 /etc/rcS.d/ 文件夹内所有程序的脚本。
其中,你会发现相继的程序会负责:
  • 配置控制台键盘;
  • 加载驱动:当探测到硬件,大部分内核模块通过内核自身加载;然后,自动加载 /etc/modules 中列出的模块;
  • 检查文件系统的完整性;
  • 挂载本地分区;
  • 配置网络;
  • 挂载网络文件系统(NFS)。
到了这个地步,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目录下的脚本)它们来适应不同的需求。
以 System V init 运行 Linux 的启动进程

图 9.2. 以 System V init 运行 Linux 的启动进程

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