初识Linux(十四)------ 系统服务 (daemons)

1. daemon 与服务 (service)

  简单的说,系统为了某些功能必须要提供一些服务 (不论是系统本身还是网络方面),这个服务就称为 service 。 但是 service 的提供总是需要程序的运行,否则如何执行呢?所以达成这个 service 的程序我们就称呼他为 daemon 。举例来说,达成循环型例行性工作调度服务 (service) 的程序为 crond 这个 daemon 。

  事实上,可以将这两者视为相同,因为达成某个服务是需要一个 daemon 在背景中运行, 没有这个 daemon 就不会有 service 。

  daemon 程序的命名方式,daemon 是一只程序执行后的进程,这些服务的名称被创建之后,被挂上 Linux 使用时,通常在服务的名称之后会加上一个 d ,例如计划任务命令的创建的  at, 与 cron 这两个服务, 他的程序文件名会被取为 atd 与 crond,这个 d 代表的就是 daemon 的意思。 

1.1 早期 System V 的 init 管理行为中 daemon 的主要分类 

  在Unix 的 system V 版本的那个年代,启动系统服务的管理方式被称为 SysV 的 init 脚本程序的处理方式。系统核心第一支调用的程序是 init , 然后 init 去唤起所有的系统所需要的服务。

  基本上 init 的管理机制有几个特色如下:

1. 服务的启动、关闭与观察等方式:

  所有的服务启动脚本通通放置于 /etc/init.d/ 下面,基本上都是使用 bash shell script 所写成的脚本程序,需要启动、关闭、重新启动、观察状态时, 可以通过如下的方式来处理:

  • 启动:/etc/init.d/daemon start
  • 关闭:/etc/init.d/daemon stop
  • 重新启动:/etc/init.d/daemon restart
  • 状态观察:/etc/init.d/daemon status

2. 服务启动的分类:

  init 服务的分类中,依据服务是独立启动或被一只总管程序管理而分为两大类:

  • 独立启动模式 (stand alone):服务独立启动,该服务直接常驻于内存中,提供本机或用户的服务行为,反应速度快。
  • 总管程序 (super daemon):由特殊的 xinetd 或 inetd 这两个总管程序提供 socket 对应或 port 对应的管理。当没有用户要求某 socket 或 port 时, 所需要的服务是不会被启动的。若有用户要求时, xinetd 总管才会去唤醒相对应的服务程序。当该要求结束时,这个服务也会被结束掉~ 因为通过 xinetd 所总管,因此这个家伙就被称为 super daemon。好处是可以通过 super daemon 来进行服务的时程、连线需求等的控制,缺点是唤醒服务需要一点时间的延迟。

3. 服务的相依性问题:

  服务是可能会有相依性的~例如,你要启动网络服务,但是系统没有网络, 那怎么可能可以唤醒网络服务呢?如果你需要连线到外部取得认证服务器的连线,但该连线需要另一个A服务的需求,问题是,A服务没有启动, 因此,你的认证服务就不可能会成功启动的,这就是所谓的服务相依性问题。init 在管理员自己手动处理这些服务时,是没有办法协助相依服务的唤醒的。

4. 执行等级的分类:

  init 是开机后核心主动调用的, 然后 init 可以根据使用者自定义的执行等级 (runlevel) 来唤醒不同的服务,以进入不同的操作界面。基本上 Linux 提供 7 个执行等级,分别是 0, 1, 2...6 , 比较重要的是 1)单人维护模式、3)纯文本模式、5)文字加图形界面。而各个执行等级的启动脚本是通过 /etc/rc.d/rc[0-6]/SXXdaemon 链接到 /etc/init.d/daemon , 链接文件名 (SXXdaemon) 的功能为: S为启动该服务,XX是数字,为启动的顺序。由于有 SXX 的设置,因此在开机时可以“依序执行”所有需要的服务, 同时也能解决相依服务的问题。这点与管理员自己手动处理不太一样。

5. 制定执行等级默认要启动的服务:

  若要创建如上提到的 SXXdaemon 的话,不需要管理员手动创建链接文件, 通过如下的指令可以来处理默认启动、默认不启动、观察默认启动否的行为:

  • 默认要启动: chkconfig daemon on
  • 默认不启动: chkconfig daemon off
  • 观察默认为启动否: chkconfig --list daemon

6. 执行等级的切换行为:

  当你要从纯命令行 (runlevel 3) 切换到图形界面 (runlevel 5), 不需要手动启动、关闭该执行等级的相关服务,只要“ init 5 ”即可切换,init 会主动去分析 /etc/rc.d/rc[35].d/ 这两个目录内的脚本, 然后启动转换 runlevel 中需要的服务~就完成整体的 runlevel 切换。

  如上就是 init 主要的功能了,重要的指令包括 daemon 本身自己的脚本 (/etc/init.d/daemon) 、xinetd 这个特殊的总管程序 (super daemon)、设置默认开机启动的 chkconfig, 以及会影响到执行等级的 init N 等。虽然 CentOS 7 已经不使用 init 来管理服务了,不过因为考虑到某些脚本没有办法直接塞入 systemd 的处理,因此这些脚本还是被保留下来。

1.2 systemd 使用的 unit 分类

  从 CentOS 7.x 以后,Red Hat 系列的 distribution 放弃沿用多年的 System V 开机启动服务的流程,就是前一小节提到的 init 启动脚本的方法, 改用 systemd 这个启动服务管理机制~那么 systemd 有什么好处呢?

1. 并行处理所有服务,加速开机流程

  旧的 init 启动脚本是“一项一项任务依序启动”的模式,因此不相依的服务也是得要一个一个的等待。但目前我们的硬件主机系统与操作系统几乎都支持多核心架构了, systemd 就是可以让所有的服务同时启动,加快系统启动的速度。

2. 即时响应的 on-demand 启动方式

  systemd 全部就是仅有一只 systemd 服务搭配 systemctl 指令来处理,无须其他额外的指令来支持。不像 systemV 还要 init, chkconfig, service... 等等指令。 此外, systemd 由于常驻内存,因此任何要求 (on-demand) 都可以立即处理后续的 daemon 启动的任务。

3. 服务相依性的自我检查

  由于 systemd 可以自定义服务相依性的检查,因此如果 B 服务是架构在 A 服务上面启动的,那当你在没有启动 A 服务的情况下仅手动启动 B 服务时, systemd 会自动帮你启动 A 服务,这样就可以免去管理员得要一项一项服务去分析的麻烦。

4. 依 daemon 功能分类

  systemd 管理的服务非常多,为了区分所有服务的功能,因此,首先 systemd 先定义所有的服务为一个服务单位 (unit),并将该 unit 归类到不同的服务类型 (type) 去。 旧的 init 仅分为 stand alone 与 super daemon ,systemd 将服务单位 (unit) 区分为 service, socket, target, path, snapshot, timer 等多种不同的类型(type), 方便管理员的分类与记忆。

5. 将多个 daemons 集合成为一个群组

  如同 systemV 的 init 里头有个 runlevel 的特色,systemd 亦将许多的功能集合成为一个所谓的 target 项目,这个项目主要在设计操作环境的创建, 所以是集合了许多的 daemons,亦即是执行某个 target 就是执行好多个 daemon 的意思。

6. 向下相容旧有的 init 服务脚本

  基本上, systemd 是可以相容于 init 的启动脚本的,因此,旧的 init 启动脚本也能够通过 systemd 来管理,只是更进阶的 systemd 功能就没有办法支持了。
虽然如此,不过 systemd 也是有些地方无法完全取代 init 的,包括:(下面的内容可能目前版本支持了,仅考虑在Centos7版本)

  • 在 runlevel 的对应上,大概仅有 runlevel 1, 3, 5 有对应到 systemd 的某些 target 类型而已,没有全部对应;
    全部的 systemd 都用 systemctl 这个管理程序管理,而 systemctl 支持的语法有限制,不像 /etc/init.d/daemon 就是纯脚本可以自定义参数,systemctl 不可自定义参数。;
  • 如果某个服务启动是管理员自己手动执行启动,而不是使用 systemctl 去启动的 (例如你自己手动输入 crond 以启动 crond 服务),那么 systemd 将无法监测到该服务,而无法进一步管理。
  • systemd 启动过程中,无法与管理员通过 standard input 传入信息。因此,自行编写 systemd 的启动设置时,务必要取消互动机制~(连通过启动时传进的标准输入信息也要避免。)

systemd 的配置文件放置目录

  基本上, systemd 将过去的 daemon 执行脚本通通称为一个服务单位 (unit),而每种服务单位依据功能来区分时,就分类为不同的类型 (type)。 基本的类型有包括系统服务、数据监听与交换的插槽文档服务 (socket)、储存系统状态的快照类型、提供不同类似执行等级分类的操作环境 (target) 等等。 配置文件都放置在下面的目录中:

  • /usr/lib/systemd/system/:每个服务最主要的启动脚本设置,有点类似以前的 /etc/init.d 下面的文件;
  • /run/systemd/system/:系统执行过程中所产生的服务脚本,这些脚本的优先序要比 /usr/lib/systemd/system/ 高;
  • /etc/systemd/system/:管理员依据主机系统的需求所创建的执行脚本,其实这个目录有点像以前 /etc/rc.d/rc5.d/Sxx 之类的功能,执行优先序又比 /run/systemd/system/ 高。

  系统开机会不会执行某些服务其实是看 /etc/systemd/system/ 下面的设置,该目录下面是一大堆链接文件。而实际执行的 systemd 启动脚本配置文件, 其实都是放置在 /usr/lib/systemd/system/ 下面的。因此如果你想要修改某个服务启动的设置,应该要去 /usr/lib/systemd/system/ 下面修改。 /etc/systemd/system/ 仅是链接到正确的执行脚本配置文件而已。


systemd 的 unit 类型分类说明

  那 /usr/lib/systemd/system/ 以下的数据如何区分上述所谓的不同的类型 (type) 呢?看扩展名!举例来说, vsftpd 这个范例的启动脚本设置, 还有 crond 与纯文本模式的 multi-user 设置:

[root@study ~]# ll /usr/lib/systemd/system/ | grep -E '(vsftpd|multi|cron)'
-rw-r--r--. 1 root root  284  7月 30  2014 crond.service
-rw-r--r--. 1 root root  567  3月  6 06:51 multipathd.service
-rw-r--r--. 1 root root  524  3月  6 13:48 multi-user.target
drwxr-xr-x. 2 root root 4096  5月  4 17:52 multi-user.target.wants
lrwxrwxrwx. 1 root root   17  5月  4 17:52 runlevel2.target -> multi-user.target
lrwxrwxrwx. 1 root root   17  5月  4 17:52 runlevel3.target -> multi-user.target
lrwxrwxrwx. 1 root root   17  5月  4 17:52 runlevel4.target -> multi-user.target
-rw-r--r--. 1 root root  171  6月 10  2014 vsftpd.service
-rw-r--r--. 1 root root  184  6月 10  2014 vsftpd@.service
-rw-r--r--. 1 root root   89  6月 10  2014 vsftpd.target
# 比较重要的是第2、4、9行

  vsftpd 与 crond 其实算是系统服务 (service),而 multi-user 要算是执行环境相关的类型 (target type)。根据这些扩展名的类型, 我们大概可以找到几种比较常见的 systemd 的服务类型如下: 

  接下来,介绍如何管理这些服务的启动与关闭。

2. 通过 systemctl 管理服务

  systemd 全部的行为都得要使用 systemctl。

2.1 通过 systemctl 管理单一服务 (service unit) 的启动/开机启动与观察状态 

  一般来说,服务启动有两种,一种是开机是否自启动,另一种是开机以后手动启动。

[root@study ~]# systemctl [command] [unit]
command 主要有:
start     :立刻启动后面接的 unit
stop      :立刻关闭后面接的 unit
restart   :立刻关闭后启动后面接的 unit,亦即执行 stop 再 start 的意思
reload    :不关闭后面接的 unit 的情况下,重新载入配置文件,让设置生效
enable    :设置下次开机时,后面接的 unit 会被启动
disable   :设置下次开机时,后面接的 unit 不会被启动
status    :目前后面接的这个 unit 的状态,会列出有没有正在执行、开机默认执行否、登录等信息等!
is-active :目前有没有正在运行中
is-enable :开机时有没有默认要启用这个 unit
范例一:看看目前 atd 这个服务的状态为何?
[root@study ~]# systemctl status atd.service
atd.service - Job spooling tools
   Loaded: loaded (/usr/lib/systemd/system/atd.service; enabled)
   Active: active (running) since Mon 2015-08-10 19:17:09 CST; 5h 42min ago
 Main PID: 1350 (atd)
   CGroup: /system.slice/atd.service
           └─1350 /usr/sbin/atd -f

Aug 10 19:17:09 study.centos.vbird systemd[1]: Started Job spooling tools.
# 重点在第四、五行
# Loaded:说明,开机的时候这个 unit 会不会启动,enabled 为开机启动,disabled 开机不会启动
# Active:现在这个 unit 的状态是正在执行 (running) 或没有执行 (dead)
# 后面几行则是说明这个 unit 程序的 PID 状态以及最后一行显示这个服务的登录文件信息。
# 登录文件信息格式为:“时间” “信息发送主机” “哪一个服务的信息” “实际信息内容”
# 所以上面的显示信息是:这个 atd 默认开机就启动,而且现在正在运行的意思!

范例二:正常关闭这个 atd 服务
[root@study ~]# systemctl stop atd.service
[root@study ~]# systemctl status atd.service
atd.service - Job spooling tools
   Loaded: loaded (/usr/lib/systemd/system/atd.service; enabled)
   Active: inactive (dead) since Tue 2015-08-11 01:04:55 CST; 4s ago
  Process: 1350 ExecStart=/usr/sbin/atd -f $OPTS (code=exited, status=0/SUCCESS)
 Main PID: 1350 (code=exited, status=0/SUCCESS)

Aug 10 19:17:09 study.centos.vbird systemd[1]: Started Job spooling tools.
Aug 11 01:04:55 study.centos.vbird systemd[1]: Stopping Job spooling tools...
Aug 11 01:04:55 study.centos.vbird systemd[1]: Stopped Job spooling tools.
# 目前这个 unit 下次开机还是会启动,但是现在是没在运行的状态中!同时,
# 最后两行为新增加的登录信息,告诉我们目前的系统状态

  回到 systemctl status atd.service 的第三行,不是有个 Active 的 daemon 现在状态吗?除了 running 跟 dead 之外, 基本上还有几个常见的状态:

  • active (running):正有一只或多只程序正在系统中执行。
  • active (exited):仅执行一次就正常结束的服务,目前并没有任何程序在系统中执行。 举例来说,开机或者是挂载时才会进行一次的 quotaon 功能,就是这种模式。quotaon 不会一直执行,只须执行一次之后,就交给文件系统去自行处理。通常用 bash shell 写的小型服务,大多是属于这种类型 (无须常驻内存)。
  • active (waiting):正在执行当中,不过还再等待其他的事件才能继续处理。举例来说,打印的队列相关服务就是这种状态。虽然正在启动中,不过,也需要真的有队列进来 (打印工作) 这样他才会继续唤醒打印机服务来进行下一步打印的功能。
  • inactive:这个服务目前没有运行。 

  daemon 的状态有:

  • enabled:这个 daemon 将在开机时被执行;
  • disabled:这个 daemon 在开机时不会被执行;
  • static:这个 daemon 不可以自己启动 (enable 不可),不过可能会被其他的 enabled 的服务来唤醒 (相依属性的服务);
  • mask:这个 daemon 无论如何都无法被启动!因为已经被强制注销 (非删除)。可通过 systemctl unmask 方式改回原本状态。 

服务启动/关闭与观察 范例

  找到系统中名为 chronyd 的服务,观察此服务的状态,将此服务设置为:开机不会启动。

# 1. 观察一下状态,确认是否为关闭/未启动
[root@study ~]# systemctl status chronyd.service
hronyd.service - NTP client/server
   Loaded: loaded (/usr/lib/systemd/system/chronyd.service; enabled)
   Active: active (running) since Mon 2015-08-10 19:17:07 CST; 24h ago
.....(下面省略).....

# 2. 由上面知道目前是启动的,因此立刻将他关闭,同时开机不会启动
[root@study ~]# systemctl stop chronyd.service
[root@study ~]# systemctl disable chronyd.service
rm '/etc/systemd/system/multi-user.target.wants/chronyd.service'
# 其实就是从 /etc/systemd/system 下面删除一条链接文件

[root@study ~]# systemctl status chronyd.service
chronyd.service - NTP client/server
   Loaded: loaded (/usr/lib/systemd/system/chronyd.service; disabled)
   Active: inactive (dead)
# 如此则将 chronyd 这个服务完整的关闭了

强迫服务注销 (mask) 

  比较正规的作法是,要关闭 cups.service 时,连同其他两个会唤醒 service 的 cups.socket 与 cups.path 通通关闭。比较不正规的作法是,那就强迫 cups.service 注销。

# 1. 保持刚刚的状态,关闭 cups.service,启动 cups.socket,然后注销 cups.servcie
[root@study ~]# systemctl stop cups.service
[root@study ~]# systemctl mask cups.service
ln -s '/dev/null' '/etc/systemd/system/cups.service'
# 其实这个 mask 注销的动作,只是让启动的脚本变成空的设备而已

[root@study ~]# systemctl status cups.service
cups.service
   Loaded: masked (/dev/null)
   Active: inactive (dead) since Tue 2015-08-11 23:14:16 CST; 52s ago

[root@study ~]# systemctl start cups.service
Failed to issue method call: Unit cups.service is masked.  # 再也无法唤醒

  那如何取消注销呢? unmask 即可。

[root@study ~]# systemctl unmask cups.service
rm '/etc/systemd/system/cups.service'
[root@study ~]# systemctl status cups.service
cups.service - CUPS Printing Service
   Loaded: loaded (/usr/lib/systemd/system/cups.service; disabled)
   Active: inactive (dead) since Tue 2015-08-11 23:14:16 CST; 4min 35s ago

2.2 通过 systemctl 观察系统上所有的服务

  通过 list-units 及 list-unit-files 来观察。

[root@study ~]# systemctl [command] [--type=TYPE] [--all]
command:
    list-units      :依据 unit 列出目前有启动的 unit。若加上 --all 才会列出没启动的。
    list-unit-files :依据 /usr/lib/systemd/system/ 内的文件,将所有文件列表说明。
--type=TYPE:就是之前提到的 unit type,主要有 service, socket, target 等
范例一:列出系统上面有启动的 unit
[root@study ~]# systemctl
UNIT                      LOAD   ACTIVE SUB       DESCRIPTION
proc-sys-fs-binfmt_mis... loaded active waiting   Arbitrary Executable File Formats File System
sys-devices-pc...:0:1:... loaded active plugged   QEMU_HARDDISK
sys-devices-pc...0:1-0... loaded active plugged   QEMU_HARDDISK
sys-devices-pc...0:0-1... loaded active plugged   QEMU_DVD-ROM
.....(中间省略).....
vsftpd.service            loaded active running   Vsftpd ftp daemon
.....(中间省略).....
cups.socket               loaded failed failed    CUPS Printing Service Sockets
.....(中间省略).....
LOAD   = Reflects whether the unit definition was properly loaded.
ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
SUB    = The low-level unit activation state, values depend on unit type.

141 loaded units listed. Pass --all to see loaded but inactive units, too.
To show all installed unit files use 'systemctl list-unit-files'.
# 列出的项目中,主要的意义是:
# UNIT   :项目的名称,包括各个 unit 的类别 (看扩展名)
# LOAD   :开机时是否会被载入,默认 systemctl 显示的是有载入的项目
# ACTIVE :目前的状态,须与后续的 SUB 搭配,就是我们用 systemctl status 观察时,active 的项目。
# DESCRIPTION :详细描述
# systemctl 都不加参数,其实默认就是 list-units 的意思

范例二:列出所有已经安装的 unit 有哪些?
[root@study ~]# systemctl list-unit-files
UNIT FILE                                   STATE
proc-sys-fs-binfmt_misc.automount           static
dev-hugepages.mount                         static
dev-mqueue.mount                            static
proc-fs-nfsd.mount                          static
.....(中间省略).....
systemd-tmpfiles-clean.timer                static

336 unit files listed.

2.3 通过 systemctl 管理不同的操作环境 (target unit)

  列出跟操作界面比较有关的 target 项目:

[root@study ~]# systemctl list-units --type=target --all
UNIT                   LOAD   ACTIVE   SUB    DESCRIPTION
basic.target           loaded active   active Basic System
cryptsetup.target      loaded active   active Encrypted Volumes
emergency.target       loaded inactive dead   Emergency Mode
final.target           loaded inactive dead   Final Step
getty.target           loaded active   active Login Prompts
graphical.target       loaded active   active Graphical Interface
local-fs-pre.target    loaded active   active Local File Systems (Pre)
local-fs.target        loaded active   active Local File Systems
multi-user.target      loaded active   active Multi-User System
network-online.target  loaded inactive dead   Network is Online
network.target         loaded active   active Network
nss-user-lookup.target loaded inactive dead   User and Group Name Lookups
paths.target           loaded active   active Paths
remote-fs-pre.target   loaded active   active Remote File Systems (Pre)
remote-fs.target       loaded active   active Remote File Systems
rescue.target          loaded inactive dead   Rescue Mode
shutdown.target        loaded inactive dead   Shutdown
slices.target          loaded active   active Slices
sockets.target         loaded active   active Sockets
sound.target           loaded active   active Sound Card
swap.target            loaded active   active Swap
sysinit.target         loaded active   active System Initialization
syslog.target          not-found inactive dead   syslog.target
time-sync.target       loaded inactive dead   System Time Synchronized
timers.target          loaded active   active Timers
umount.target          loaded inactive dead   Unmount All Filesystems

LOAD   = Reflects whether the unit definition was properly loaded.
ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
SUB    = The low-level unit activation state, values depend on unit type.

26 loaded units listed.
To show all installed unit files use 'systemctl list-unit-files'.

  在CentOS 7.1 的默认情况下,有 26 个 target unit ,而跟操作界面相关性比较高的 target 主要有下面几个: 

  • graphical.target:就是文字加上图形界面,这个项目已经包含了下面的 multi-user.target 项目;
  • multi-user.target:纯文本模式;
  • rescue.target:在无法使用 root 登陆的情况下,systemd 在开机时会多加一个额外的暂时系统,与你原本的系统无关。这时你可以取得 root 的权限来维护你的系统。 但是这是额外系统,因此可能需要动到 chroot 的方式来取得你原有的系统;
  • emergency.target:紧急处理系统的错误,还是需要使用 root 登陆的情况,在无法使用 rescue.target 时,可以尝试使用这种模式;
  • shutdown.target:就是关机的流程。
  • getty.target:可以设置你需要几个 tty 之类的,如果想要降低 tty 的项目,可以修改这个东西的配置文件。

  正常的模式是 multi-user.target 以及 graphical.target 两个,救援方面的模式主要是 rescue.target 以及更严重的 emergency.target。 如果要修改可提供登陆的 tty 数量,则修改 getty.target 项目。基本上,我们最常使用的当然就是 multi-user 以及 graphical。

  那么如何知道目前的模式是哪一种?又得要如何修改呢?

[root@study ~]# systemctl [command] [unit.target]
选项与参数:
command:
    get-default :取得目前的 target 
    set-default :设置后面接的 target 成为默认的操作模式
    isolate     :切换到后面接的模式

范例一:测试机器默认是图形界面,先观察默认模式
[root@study ~]# systemctl get-default 
graphical.target  # 图形界面

[root@study ~]# systemctl set-default multi-user.target
[root@study ~]# systemctl get-default 
multi-user.target

范例二:在不重新开机的情况下,将目前的操作环境改为纯文本模式,关掉图形界面
[root@study ~]# systemctl isolate multi-user.target

范例三:若需要重新取得图形界面
[root@study ~]# systemctl isolate graphical.target

  在正常的切换情况下,使用上述 isolate 的方式即可。不过为了方便起见, systemd 也提供了数个简单的指令给我们切换操作模式之用,大致上如下所示: 

[root@study ~]# systemctl poweroff  系统关机
[root@study ~]# systemctl reboot    重新开机
[root@study ~]# systemctl suspend   进入暂停模式
[root@study ~]# systemctl hibernate 进入休眠模式
[root@study ~]# systemctl rescue    强制进入救援模式
[root@study ~]# systemctl emergency 强制进入紧急救援模式
  • suspend:暂停模式会将系统的状态数据保存到内存中,然后关闭掉大部分的系统硬件,当然,并没有实际关机,当使用者按下唤醒机器的按钮,系统数据会重内存中回复,然后重新驱动被大部分关闭的硬件,就开始正常运行,唤醒的速度较快。
  • hibernate:休眠模式则是将系统状态保存到硬盘当中,保存完毕后,将计算机关机。当使用者尝试唤醒系统时,系统会开始正常运行, 然后将保存在硬盘中的系统状态恢复回来。因为数据是由硬盘读出,因此唤醒的性能与你的硬盘速度有关。

2.4 通过 systemctl 分析各服务之间的相依性

  如何追踪某一个 unit 的相依性呢? 举例来说好了,我们怎么知道 graphical.target 会用到 multi-user.target 呢?那 graphical.target 下面还有哪些东西呢?

[root@study ~]# systemctl list-dependencies [unit] [--reverse]
选项与参数:
--reverse :反向追踪谁使用这个 unit 的意思
范例一:列出目前的 target 环境下,用到什么特别的 unit 
[root@study ~]# systemctl get-default
multi-user.target

[root@study ~]# systemctl list-dependencies
default.target
├─abrt-ccpp.service
├─abrt-oops.service
├─vsftpd.service
├─basic.target
│ ├─alsa-restore.service
│ ├─alsa-state.service
.....(中间省略).....
│ ├─sockets.target
│ │ ├─avahi-daemon.socket
│ │ ├─dbus.socket
.....(中间省略).....
│ ├─sysinit.target
│ │ ├─dev-hugepages.mount
│ │ ├─dev-mqueue.mount
.....(中间省略).....
│ └─timers.target
│   └─systemd-tmpfiles-clean.timer
├─getty.target
│ └─getty@tty1.service
└─remote-fs.target

  根据连线的流程, multi-user.target 其实还会用到 basic.target + getty.target + remote-fs.target 三大项目, 而 basic.target 又用到了 sockets.target + sysinit.target + timers.target... 等一堆~所以,能够清楚的查询到每种 target 模式下面还有的相依模式。 那么如果要查出谁会用到 multi-user.target 呢?

[root@study ~]# systemctl list-dependencies --reverse
default.target
└─graphical.target
[root@study ~]# systemctl list-dependencies graphical.target
graphical.target
├─accounts-daemon.service
├─gdm.service
├─network.service
├─rtkit-daemon.service
├─systemd-update-utmp-runlevel.service
└─multi-user.target
  ├─abrt-ccpp.service
  ├─abrt-oops.service
.....(下面省略).....

2.5 与 systemd 的 daemon 运行过程相关的目录简介

  比较重要的 systemd 启动脚本配置文件在 /usr/lib/systemd/system/, /etc/systemd/system/ 目录下,那还有哪些目录跟系统的 daemon 运行有关呢? 基本上是这样的:

  •  /usr/lib/systemd/system/
    使用 CentOS 官方提供的软件安装后,默认的启动脚本配置文件都放在这里,这里的数据尽量不要修改~ 要修改时,请到 /etc/systemd/system 下面修改较佳;
  • /run/systemd/system/
    系统执行过程中所产生的服务脚本,这些脚本的优先序要比 /usr/lib/systemd/system/ 高;
  • /etc/systemd/system/
    管理员依据主机系统的需求所创建的执行脚本,其实这个目录有点像以前 /etc/rc.d/rc5.d/Sxx 之类的功能,执行优先序又比 /run/systemd/system/ 高;
  • /etc/sysconfig/*
    几乎所有的服务都会将初始化的一些选项设置写入到这个目录下,举例来说,mandb 所要更新的 man page 索引中,需要加入的参数就写入到此目录下的 man-db 当中,而网络的设置则写在 /etc/sysconfig/network-scripts/ 这个目录内。所以,这个目录内的文件也是挺重要的;
  • /var/lib/
    一些会产生数据的服务都会将他的数据写入到 /var/lib/ 目录中。举例来说,数据库管理系统 Mariadb 的数据库默认就是写入 /var/lib/mysql/ 这个目录下;
  • /run/
    放置了好多 daemon 的缓存,包括 lock file 以及 PID file 等等。

   systemd 有很多会用到的 socket 服务,可能会产生很多的 socket file,这些 socket file 还是通过 systemctl 来管理。

[root@study ~]# systemctl list-sockets
LISTEN                          UNIT                         ACTIVATES
/dev/initctl                    systemd-initctl.socket       systemd-initctl.service
/dev/log                        systemd-journald.socket      systemd-journald.service
/run/dmeventd-client            dm-event.socket              dm-event.service
/run/dmeventd-server            dm-event.socket              dm-event.service
/run/lvm/lvmetad.socket         lvm2-lvmetad.socket          lvm2-lvmetad.service
/run/systemd/journal/socket     systemd-journald.socket      systemd-journald.service
/run/systemd/journal/stdout     systemd-journald.socket      systemd-journald.service
/run/systemd/shutdownd          systemd-shutdownd.socket     systemd-shutdownd.service
/run/udev/control               systemd-udevd-control.socket systemd-udevd.service
/var/run/avahi-daemon/socket    avahi-daemon.socket          avahi-daemon.service
/var/run/cups/cups.sock         cups.socket                  cups.service
/var/run/dbus/system_bus_socket dbus.socket                  dbus.service
/var/run/rpcbind.sock           rpcbind.socket               rpcbind.service
@ISCSIADM_ABSTRACT_NAMESPACE    iscsid.socket                iscsid.service
@ISCSID_UIP_ABSTRACT_NAMESPACE  iscsiuio.socket              iscsiuio.service
kobject-uevent 1                systemd-udevd-kernel.socket  systemd-udevd.service

16 sockets listed.
Pass --all to see loaded but inactive sockets, too.

  这样很清楚的就能够知道正在监听本机服务需求的 socket file 所在的文件名位置。


网络服务与端口对应简介

  服务与端口对应 /etc/services  

[root@study ~]# cat /etc/services
....(前面省略)....
ftp             21/tcp
ftp             21/udp          fsp fspd
ssh             22/tcp                          # The Secure Shell (SSH) Protocol
ssh             22/udp                          # The Secure Shell (SSH) Protocol
....(中间省略)....
http            80/tcp          www www-http    # WorldWideWeb HTTP
http            80/udp          www www-http    # HyperText Transfer Protocol
....(下面省略)....
# 这个文件的内容是以下面的方式来编排的:
# <daemon name>   <port/封包协定>   <该服务的说明>

  不建议直接在这里修改服务的端口号,可能会报错。

2.6 关闭网络服务

  当使用 systemctl 去观察本机服务器启动的服务时,怎么 CentOS 7.x 启动了几乎 100 多个以上的 daemon? 这是因为 systemd 将许多原本不被列为 daemon 的程序都纳入到 systemd 自己的管辖监测范围内,大部分都属于 Linux 系统基础运行所需要的环境,没有什么特别需求的话,最好都不要更动。

  除了本机服务之外,还有网络服务,网络服务默认由 SELinux 管理。那么什么是网络服务呢?基本上,会产生一个网络监听端口 (port) 的进程,就可以称他是个网络服务。那么如何观察网络端口?

[root@study ~]# netstat -tlunp
Proto Recv-Q Send-Q Local Address   Foreign Address  State    PID/Program name
tcp        0      0 0.0.0.0:22      0.0.0.0:*        LISTEN   1340/sshd
tcp        0      0 127.0.0.1:25    0.0.0.0:*        LISTEN   2387/master
tcp6       0      0 :::555          :::*             LISTEN   29113/vsftpd
tcp6       0      0 :::22           :::*             LISTEN   1340/sshd
tcp6       0      0 ::1:25          :::*             LISTEN   2387/master
udp        0      0 0.0.0.0:5353    0.0.0.0:*                 750/avahi-daemon: r
udp        0      0 0.0.0.0:36540   0.0.0.0:*                 750/avahi-daemon: r

  如上所示,系统占用了 22, 25, 555, 5353, 36540 这几个端口,而其中 5353, 36540 是由 avahi-daemon 启动的。使用 systemctl 去观察一下,到底有没有 avahi-daemon 为开头的服务呢?

[root@study ~]# systemctl list-units --all | grep avahi-daemon
avahi-daemon.service   loaded active   running   Avahi mDNS/DNS-SD Stack
avahi-daemon.socket    loaded active   running   Avahi mDNS/DNS-SD Stack Activation Socket

  avahi-daemon 的目的是在局域网络进行类似网芳的搜寻,因此这个服务可以协助你在区网内随时了解即插即用的装置,包括笔记本电脑等,只要连上你的区网,你就能够知道谁进来了。这个服务可有可无,这里关闭。

[root@study ~]# systemctl stop avahi-daemon.service
[root@study ~]# systemctl stop avahi-daemon.socket
[root@study ~]# systemctl disable avahi-daemon.service avahi-daemon.socket
[root@study ~]# netstat -tlunp
Proto Recv-Q Send-Q Local Address   Foreign Address  State    PID/Program name
tcp        0      0 0.0.0.0:22      0.0.0.0:*        LISTEN   1340/sshd
tcp        0      0 127.0.0.1:25    0.0.0.0:*        LISTEN   2387/master
tcp6       0      0 :::555          :::*             LISTEN   29113/vsftpd
tcp6       0      0 :::22           :::*             LISTEN   1340/sshd
tcp6       0      0 ::1:25          :::*             LISTEN   2387/master

3. systemctl 针对 service 类型的配置文件

  以前,我们如果想要创建系统服务,就得要到 /etc/init.d/ 下面去创建相对应的 bash shell script 来处理。那么现在 systemd 的环境下面, 如果我们想要设置相关的服务启动环境,那应该如何处理呢?

3.1 systemctl 配置文件相关目录简介 

  服务的管理是通过 systemd,而 systemd 的配置文件大部分放置于 /usr/lib/systemd/system/ 目录内。但是 Red Hat 官方文件指出, 该目录的文件主要是原本软件所提供的设置,建议不要修改,而要修改的位置应该放置于 /etc/systemd/system/ 目录内。举例来说,如果你想要额外修改 vsftpd.service 的话, 他们建议要放置到哪些地方呢?

  •  /usr/lib/systemd/system/vsftpd.service:官方释出的默认配置文件;
  • /etc/systemd/system/vsftpd.service.d/custom.conf:在 /etc/systemd/system 下面创建与配置文件相同文件名的目录,但是要加上 .d 的扩展名。然后在该目录下创建配置文件即可。另外,配置文件最好附文件名取名为 .conf 较佳。在这个目录下的文件会“累加其他设置”进入 /usr/lib/systemd/system/vsftpd.service 内。
  • /etc/systemd/system/vsftpd.service.wants/*:此目录内的文件为链接文件,设置相依服务的链接。意思是启动了 vsftpd.service 之后,最好再加上这目录下面建议的服务。
  • /etc/systemd/system/vsftpd.service.requires/*:此目录内的文件为链接文件,设置相依服务的链接。意思是在启动 vsftpd.service 之前,需要事先启动哪些服务的意思。

  基本上,在配置文件里面你都可以自由设置相依服务的检查,并且设置加入到哪些 target 里头去。但是如果是已经存在的配置文件,或者是官方提供的配置文件, Red Hat 是建议你不要修改原设置,而是到上面提到的几个目录去进行额外的客制化设置比较好。如果你硬要修改原始的 /usr/lib/systemd/system 下面的配置文件,那也没问题,并且也能够减少许多配置文件。

3.2 systemctl 配置文件的设置项目简介sshd.service 

  了解了配置文件的相关目录与文件之后,还得要了解一下配置文件本身的内容。以 sshd.service 为例:

[root@study ~]# cat /usr/lib/systemd/system/sshd.service
[Unit]           # 这个项目与此 unit 的解释、执行服务相依性有关
Description=OpenSSH server daemon
After=network.target sshd-keygen.service
Wants=sshd-keygen.service

[Service]        # 这个项目与实际执行的指令参数有关
EnvironmentFile=/etc/sysconfig/sshd
ExecStart=/usr/sbin/sshd -D $OPTIONS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=42s

[Install]        # 这个项目说明此 unit 要挂载哪个 target 下面
WantedBy=multi-user.target

  分析上面的配置文件,我们大概能够将整个设置分为三个部份,就是:

  • [Unit]: unit 本身的说明,以及与其他相依 daemon 的设置,包括在什么服务之后才启动此 unit 之类的设置值;
  • [Service], [Socket], [Timer], [Mount], [Path]..:不同的 unit type 就得要使用相对应的设置项目。我们拿的是 sshd.service 来当范本,所以这边就使用 [Service] 来设置。 这个项目内主要在规范服务启动的脚本、环境配置文件文件名、重新启动的方式等等。
  • [Install]:这个项目就是将此 unit 安装到哪个 target 里面去的意思

  配置文件内有些设置的规则 :

  • 设置项目通常是可以重复的,例如我可以重复设置两个 After 在配置文件中,不过,后面的设置会取代前面的。如果你想要将设置值归零, 可以使用类似“ After= ”的设置。
  • 如果设置参数需要有“是/否”的项目 (布尔值, boolean),可以使用 1, yes, true, on 代表启动,用 0, no, false, off 代表关闭。
  • 空白行、开头为 # 或 ; 的那一行,都代表注释。

  每个部份里面还有很多的设置细项:

  接下来了解一下在 [Service] 当中有哪些项目可以使用:

  最后, Install 内还有哪些项目可用?

3.3 多重的重复设置方式:以 getty 为例

  centos 7 有 6 个终端机可用,tty1 - 6,由 agetty 这个指令达成。主要管理的是 getty.target 的 target unit 。不过,实际产生 tty1~tty6 的则是由 getty@.service 所提供的。查阅一下 /usr/lib/systemd/system/getty@.service 的内容:

[root@study ~]# cat //usr/lib/systemd/system/getty@.service
[Unit]
Description=Getty on %I
Documentation=man:agetty(8) man:systemd-getty-generator(8)
Documentation=http://0pointer.de/blog/projects/serial-console.html
After=systemd-user-sessions.service plymouth-quit-wait.service
After=rc-local.service
Before=getty.target
ConditionPathExists=/dev/tty0

[Service]
ExecStart=-/sbin/agetty --noclear %I $TERM
Type=idle
Restart=always
RestartSec=0
UtmpIdentifier=%I
TTYPath=/dev/%I
TTYReset=yes
TTYVHangup=yes
TTYVTDisallocate=yes
KillMode=process
IgnoreSIGPIPE=no
SendSIGHUP=yes

[Install]
WantedBy=getty.target

  比较重要的是 ExecStart 项目,当 man agetty 时,发现到它的语法应该是“ agetty --noclear tty1 ”之类的字样。因此,如果要启动六个 tty 的时候,基本上应该要有六个启动配置文件。亦即是可能会用到 getty1.service, getty2.service...getty6.service 才对。这样控管很麻烦~所以,才会出现这个 @ 的项目。先来看看 getty@.service 的上级,亦即是 getty.target 的内容:

[root@study ~]# systemctl show getty.target
# 那个 show 的指令可以将 getty.target 的默认设置值也取出来显示!
Names=getty.target
Wants=getty@tty1.service
WantedBy=multi-user.target
Conflicts=shutdown.target
Before=multi-user.target
After=getty@tty1.service getty@tty2.service getty@tty3.service getty@tty4.service 
  getty@tty6.service getty@tty5.service
.....(后面省略).....

  在第八行,多出六个的 service ,以 getty@tty1.service 为例,当执行完 getty.target 之后, 会要求 getty@tty1.service 等六个服务继续启动。systemd 执行步骤:

  • 先看 /usr/lib/systemd/system/, /etc/systemd/system/ 有没有 getty@tty1.service 的设置,若有就执行,若没有则执行下一步;
  • 找 getty@.service 的设置,若有则将 @ 后面的数据带入成 %I 的变量,进入 getty@.service 执行。

  这也就是说,getty@tty1.service 实际上是不存在的,他主要是通过 getty@.service 来执行, getty@.service 的目的是为了要简化多个执行的启动设置, 他的命名方式是这样的:

  • 原始文件:执行服务名称@.service
  • 可执行文件:执行服务名称@范例名称.service

  ExecStart=-/sbin/agetty --noclear %I $TERM

  那个%I 指的就是“范例名称”,getty@tty1.service 的 %I 就是 tty1 ,因此执行脚本就会变成“ /sbin/agetty --noclear tty1 。 


将 tty 的数量由 6 个降低到 4 个

  “6 个 tty 是谁规定的,为什么不是 5 个还是 7 个?这是因为 systemd 的登陆配置文件 /etc/systemd/logind.conf 里面规范的。假如你想要让 tty 数量降低到 4 个的话,可以这么做:

# 1. 修改默认的 logind.conf 内容,将原本 6 个虚拟终端机改成 4 个
[root@study ~]# vim /etc/systemd/logind.conf
[Login]
NAutoVTs=4
ReserveVT=0
# 原本是 6 个而且还注解,请取消注解,然后改成 4 

# 2. 关闭不小心启动的 tty5, tty6 并重新启动 getty.target 
[root@study ~]# systemctl stop getty@tty5.service
[root@study ~]# systemctl stop getty@tty6.service
[root@study ~]# systemctl restart systemd-logind.service

  再到桌面环境下,按下 [ctrl]+[alt]+[F1]~[F6] 就会发现,只剩下四个可用的 tty 。

  如果需要暂时启动 tty8 时,又该如何处理呢?需要重新创建一个脚本吗?不需要,可以这样做:

[root@study ~]# systemctl start getty@tty8.service

 


暂时新增 vsftpd 到 2121 端口 

   在 /usr/lib/systemd/system 下面还有个特别的 vsftpd@.service ,文件内容如下:

[root@study ~]# cat /usr/lib/systemd/system/vsftpd@.service
[Unit]
Description=Vsftpd ftp daemon
After=network.target
PartOf=vsftpd.target

[Service]
Type=forking
ExecStart=/usr/sbin/vsftpd /etc/vsftpd/%i.conf

[Install]
WantedBy=vsftpd.target

  根据前面 getty@.service 的说明,我们知道在启动的脚本设置当中, %i 或 %I 就是代表 @ 后面接的范例文件名的意思。那能不能创建 vsftpd3.conf 文件,然后通过该文件来启动新的服务? 

 

# 1. 根据 vsftpd@.service 的建议,于 /etc/vsftpd/ 下面先创建新的配置文件
[root@study ~]# cd /etc/vsftpd
[root@study vsftpd]# cp vsftpd.conf vsftpd3.conf
[root@study vsftpd]# vim vsftpd3.conf
listen_port=2121

# 2. 暂时启动这个服务,不要永久启动他!
[root@study vsftpd]# systemctl start vsftpd@vsftpd3.service
[root@study vsftpd]# systemctl status vsftpd@vsftpd3.service
vsftpd@vsftpd3.service - Vsftpd ftp daemon
   Loaded: loaded (/usr/lib/systemd/system/vsftpd@.service; disabled)
   Active: active (running) since Thu 2015-08-13 01:34:05 CST; 5s ago

[root@study vsftpd]# netstat -tlnp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address  Foreign Address  State    PID/Program name
tcp6       0      0 :::2121        :::*             LISTEN   16404/vsftpd
tcp6       0      0 :::555         :::*             LISTEN   12672/vsftpd
tcp6       0      0 :::21          :::*             LISTEN   12670/vsftpd

  因为我们启用了 vsftpd@vsftpd3.service ,代表要使用的配置文件在 /etc/vsftpd/vsftpd3.conf ,所以可以直接通过 vsftpd@.service 而无须重新设置启动脚本 。

 4. systemctl 针对 timer 的配置文件

  有时候,某些服务你想要定期执行,或者是开机后执行,或者是什么服务启动多久后执行等等的。在过去,大概都是使用 crond 这个服务来定期处理, 现在 systemd 有个协力服务 timers.target 可以协助定期处理各种任务。


systemd.timer 的优势

  • 由于所有的 systemd 的服务产生的信息都会被记录 (log),因此比 crond 在 debug 上面要更清楚方便的多;
  • 各项 timer 的工作可以跟 systemd 的服务相结合;
  • 各项 timer 的工作可以跟 control group (cgroup,用来取代 /etc/secure/limit.conf 的功能) 结合,来限制该工作的资源利用。

  还是有些弱点,例如 systemd 的 timer 并没有 email 通知的功能 ,也没有类似 anacron 的一段时间内的随机取样功能 (random_delay), 不过,总体来说,还是挺不错的。此外,相对于 crond 最小的单位到分, systemd 是可以到秒甚至是毫秒的单位。


任务需求

  基本上,想要使用 systemd 的 timer 功能,需要满足以下条件:

  • 系统的 timer.target 一定要启动
  • 要有个 sname.service 的服务存在 (sname 是你自己指定的名称)
  • 要有个 sname.timer 的时间启动服务存在

sname.timer 的设置值

   在 /etc/systemd/system 下面去创建这个 *.timer 文件,基本设置主要有下面这些: (man systemd.timer & man systemd.time):


使用于 OnCalendar 的时间

基本上的格式如下所示:

  语法:英文周名  YYYY-MM-DD  HH:MM:SS
  范例:  Thu        2015-08-13       13:40:00

也可以直接使用间隔时间来处理,常用的间隔时间单位有:

  • us 或 usec:微秒 (10-6 秒)
  • ms 或 msec:毫秒 (10-3 秒)
  • s, sec, second, seconds
  • m, min, minute, minutes
  • h, hr, hour, hours
  • d, day, days
  • w, week, weeks
  • month, months
  • y, year, years

常见的使用范例有:

  隔 3 小时:             3h  或 3hr 或 3hours
  隔 300 分钟过 10 秒:   10s 300m
  隔 5 天又 100 分钟:    100m 5day
  # 通常英文的写法,小单位写前面,大单位写后面~所以先秒、再分、再小时、再天数等

此外,也可以使用英文常用的口语化日期代表,例如 today, tomorrow 等。假设今天是 2015-08-13 13:50:00 的话,那么:


一个循环时间运行的案例

假设:

  • 开机后 2 小时开始执行一次这个 backup.service
  • 自从第一次执行后,未来每两天要执行一次 backup.service
[root@study ~]# vim /etc/systemd/system/backup.timer
[Unit]
Description=backup my server timer

[Timer]
OnBootSec=2hrs
OnUnitActiveSec=2days

[Install]
WantedBy=multi-user.target

[root@study ~]# systemctl daemon-reload
[root@study ~]# systemctl enable backup.timer
[root@study ~]# systemctl restart backup.timer
[root@study ~]# systemctl list-unit-files | grep backup
backup.service          disabled   # 这个不需要启动,只要 enable backup.timer 即可。
backup.timer            enabled

[root@study ~]# systemctl show timers.target
ConditionTimestamp=Thu 2015-08-13 14:31:11 CST      # timer 这个 unit 启动的时间

[root@study ~]# systemctl show backup.service
ExecMainExitTimestamp=Thu 2015-08-13 14:50:19 CST   # backup.service 上次执行的时间

[root@study ~]# systemctl show backup.timer
NextElapseUSecMonotonic=2d 19min 11.540653s         # 下一次执行距离 timers.target 的时间

一个固定日期运行的案例

  如果希望星期天凌晨 2 点都运行这个备份程序怎么做:


[root@study ~]# vim /etc/systemd/system/backup2.timer
[Unit]
Description=backup my server timer2

[Timer]
OnCalendar=Sun *-*-* 02:00:00
Persistent=true
Unit=backup.service

[Install]
WantedBy=multi-user.target

[root@study ~]# systemctl daemon-reload
[root@study ~]# systemctl enable backup2.timer
[root@study ~]# systemctl start backup2.timer
[root@study ~]# systemctl show backup2.timer
NextElapseUSecRealtime=45y 7month 1w 6d 10h 30min

  与循环时间运行差异比较大的地方,在于这个 OnCalendar 的方法对照的时间并不是 times.target 的启动时间,而是 Unix 标准时间! 亦即是 1970-01-01 00:00:00 去比较的。因此,最后出现的 NextElapseUSecRealtime ,下一次执行还要 45 年 + 7 个月 + 1 周 + 6 天 + 10 小时过 30 分。

5. CentOS 7.x 默认启动的服务简易说明

  随着 Linux 上面软件支持性越来越多,加上自由软件蓬勃的发展,我们可以在 Linux 上面用的 daemons 真的越来越多了。所以,这里仅介绍几个很常见的 daemons , 更多的信息使用 systemctl list-unit-files --type=service 去查询。下面的表格主要是针对 Linux 单机服务器的角色来说明的:

  下面继续说明一些可能在你的系统当中的服务,只是默认并没有启动这个服务。

 

posted @ 2023-01-02 16:21  莫莫君不恋爱  阅读(1182)  评论(0编辑  收藏  举报