systemd 是 Linux 系统工具,用来启动守护进程,已成为大多数发行版的标准配置。

systemd 的优点是功能强大,使用方便,缺点是体系庞大,非常复杂;systemd执行服务是并行的,如果没有人为的干预附加启动依赖,同级服务启动顺序是乱序的,这个在后续会体现出来。

systemd PID 配置 systemd详解_linux


systemd囊括的功能板块很多,内容更倾向运维工作,目前因项目需求,就服务板块进行分析。

systemd不是简单的一个命令,而是非常复杂一组命令,涵盖系统管理的方方面面。

一、systemctl

systemctl是systemd的主要命令,用于整体管理系统各项服务等。

在systemctl眼里,最小的功能单位就是单元,也是单个进程的描述,一个个小的单元互相调用和依赖,组成一个庞大的任务管理系统,这就是 Systemd 的基本思想。

通过systemctl list-unit-files可以查看当前系统中所有的单元,如下:

systemd PID 配置 systemd详解_linux_02


每个单元都有一个描述单元的文件,用于规范单元的功能和执行的机制,分别存放在三个不同的目录:

1.1 查看单元

像上述的查看系统中所有的单元文件的命令如下:

/lib/systemd/system
/etc/systemd/system
/usr/lib/systemd/system
  • /lib/systemd/system:主要存放的是系统默认的单元文件;
  • /etc/systemd/system:用户安装的软件的单元文件;
  • /usr/lib/systemd/system:用户自定义的单元文件;

1.2 单元的管理

明白了单元存在的含义,那么需要管理呀,怎么管理单元的动作,具体如下:

systemctl start [UnitName]
systemctl stop [UnitName]
systemctl restart [UnitName]
systemctl kill [UnitName]
systemctl status [UnitName]
systemctl enable [UnitName]
systemctl disable [UnitName]

顾名思义,通过systemctl控制对单元的开启、停止、重启等操作,这里着重说明的是:

  • systemctl enable [UnitName]:用来开启单元的开机自启动,这步动作会生成一个符号链接,链接到你设计的单元指定的target下面,后续展开说明;
  • systemctl disable [UnitName]:用来取消单元开机自启动;
  • 该服务需要慎用,对于某些服务来说,取消单元开机自启动时,会导致该单元某些文件的缺失,亲测!!
  • 在取消lightdm.service单元服务时,即使使用systemctl enable lightdm.service恢复开机自启动都无济于事,需要通过sudo apt-get install --reinstall lightdm进行重装。
  • systemctl status [UnitName]:查看单元当前状态,具体如下:
    通过查看status可以查看服务具体启动的信息,这个后续讲解自启动服务的时候会仔细分析到。

二、systemd-analyze

简单分析完systemctl的部分功能之后,systemd还提供了一个可以查看所有启动的总耗时的工具,systemd-analyze

systemd-analyze  
systemd-analyze blame
systemd-analyze plot > filename.svg
systemd-analyze critical-chain
systemd-analyze critical-chain atd.service
  • systemd-analyze:直接查看服务单元启动时间,如下
  • 这里不是简单的展开系统开机时间,而是包括了内核启动时间和用户启动时间,在两个不同的硬件上,可以看到,前者是虚拟机中执行的结果,后者直接搭载在板子上的linux系统甚至可以看到固件执行时间、加载时间、内核启动时间、以及用户启动时间,具体的含义有待后续进一步确认;
  • systemd-analyze blame:查看每个服务的启动耗时,该命令直接将不同服务单元的启动用时打印出来,如下:
  • 不难看出plymouth-quit-wait.service单元服务执行用时11.832秒,但是这个顺序并不代表执行顺序,而是以耗时长短排序,需要得到启动顺序,执行后面的命令;
  • systemd-analyze plot > filename.svg:通过执行该命令,可以在执行目录下生成一个filename.svg的文件,该文件建议使用浏览器打开:
  • 文件中可以清楚的看到服务单元的启动顺序,如果你多启动几次电脑,多生成几个文件进行服务启动顺序对比,你可以清晰的理解文章开头强调的systemd启动服务是乱序并行的说法。
  • systemd-analyze critical-chain:显示瀑布状的启动过程流,这个命令可以看个大概;
  • 在“@”字符之后打印单位处于活动状态或启动后的时间。
  • 在“+”字符之后,在“+”字符之后打印单位开始的时间。
  • systemd-analyze critical-chain atd.service:显示指定服务的启动流,具体看哪个服务就写哪个服务。

三、定制服务

前面介绍这么多都是纸上谈兵而已,需要真正使用上systemd的各种功能,首先要学会怎么读服务文件;

3.1 读懂服务文件

我们找一个.service文件去读,可以通过systemctl cat命令查看服务的配置文件,也可以直接到前面说的三个目录下去直接打开,这里打开rc-local.service来讲解:

#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

# This unit gets pulled automatically into multi-user.target by
# systemd-rc-local-generator if /etc/rc.local is executable.
[Unit]
Description=/etc/rc.local Compatibility
ConditionFileIsExecutable=/etc/rc.local
After=network.target

[Service]
Type=forking
ExecStart=/etc/rc.local start
TimeoutSec=0
RemainAfterExit=yes

一个服务怎么启动、什么时候启动、启动什么东西都是由他的配置文件决定的,如上所示,rc-local.service服务主要是执行/etc/rc.local的内容,这个关联到linux启动项的内容,这里不展开说明,后续会补充。

rc-local.service服务的结构分为两个板块,有[Unit][Service],其实还有另外一个[Install],后续会分别进行说明:

3.1.1 [Uint]:启动顺序和依赖关系

  • Unit区块的Description字段给出当前服务的简单描述,Documentation字段给出文档位置;
  • After字段:表示如果network.targetsshd-keygen.service需要启动,那么sshd.service应该在它们之后启动;
  • Before字段,定义sshd.service应该在哪些服务之前启动;
  • After和Before字段只涉及启动顺序,不涉及依赖关系;
  • Wants字段:表示sshd.servicesshd-keygen.service之间存在"弱依赖"关系,即如果"sshd-keygen.service"启动失败或停止运行,不影响sshd.service继续执行;
  • Requires字段则表示"强依赖"关系,即如果该服务启动失败或异常退出,那么sshd.service也必须退出;
  • Wants字段与Requires字段只涉及依赖关系,与启动顺序无关,默认情况下是同时启动的。
  • ConditionFileIsExecutable=/etc/rc.local指定执行文件;

3.1.2 [Service]:启动行为,定义如何启动当前服务

  • EnvironmentFile字段:指定当前服务的环境参数文件。该文件内部的key=value键值对,可以用$key的形式,在当前配置文件中获取;
  • ExecStart字段:定义启动进程时执行的命令;
  • ExecReload字段:重启服务时执行的命令;
  • ExecStop字段:停止服务时执行的命令;
  • ExecStartPre字段:启动服务之前执行的命令;
  • ExecStartPost字段:启动服务之后执行的命令;
  • ExecStopPost字段:停止服务之后执行的命令;
  • Type字段定义启动类型。它可以设置的值如下;
  • simple(默认值):ExecStart字段启动的进程为主进程;
  • forking:ExecStart字段将以fork()方式启动,此时父进程将会退出,子进程将成为主进程;
  • oneshot:类似于simple,但只执行一次,Systemd 会等它执行完,才启动其他服务;
  • dbus:类似于simple,但会等待 D-Bus 信号后启动;
  • notify:类似于simple,启动结束后会发出通知信号,然后 Systemd 再启动其他服务;
  • idle:类似于simple,但是要等到其他任务都执行完,才会启动该服务。一种使用场合是为让该服务的输出,不与其他服务的输出相混合;
  • KillMode字段:定义Systemd如何停止sshd服务;
  • control-group(默认值):当前控制组里面的所有子进程,都会被杀掉;
  • process:只杀主进程;
  • mixed:主进程将收到SIGTERM信号,子进程收到SIGKILL信号;
  • none:没有进程会被杀掉,只是执行服务的stop命令;
  • Restart字段:定义了sshd退出后,Systemd的重启方式;
  • no(默认值):退出后不会重启;
  • on-success:只有正常退出时(退出状态码为0),才会重启;
  • on-failure:非正常退出时(退出状态码非0),包括被信号终止和超时,才会重启;
  • on-abnormal:只有被信号终止和超时,才会重启;
  • on-abort:只有在收到没有捕捉到的信号终止时,才会重启;
  • on-watchdog:超时退出,才会重启;
  • always:不管是什么退出原因,总是重启;
  • RestartSec字段:表示 Systemd 重启服务之前,需要等待的秒数。上面的例子设为等待42秒;

3.1.3 [Install]:定义如何安装这个配置文件,即怎样做到开机启动

  • WantedBy字段:表示该服务所在的 Target;Target的含义是服务组,表示一组服务。WantedBy=multi-user.target指的是,sshd 所在的 Target 是multi-user.target

这个设置非常重要,因为执行systemctl enable sshd.service命令时,sshd.service的一个符号链接,就会放在/etc/systemd/system目录下面的multi-user.target.wants子目录之中。

分析完之后,回到[rc-local.service源码]( 3.1 读懂服务文件),我们可以清楚的解析出来,该服务指定的执行文件为/etc/rc.local,规定该服务在network.target服务执行完成之后再执行,并且该服务类型为forking,该服务启动时执行的命令为/etc/rc.local start

3.2 target 服务组

在查看/etc/systemd/system/lib/systemd/system下面的服务文件时,仔细的话可以看到分别有.service.target以及.wants三种不同后缀的文件及文件夹。

像[3.1.3 [Install]]( 3.1.3 [Install]:定义如何安装这个配置文件,即怎样做到开机启动)中描述的WantedBy字段:表示该服务所在的 target;该target指的就是.target,每个.target指的都是一个独立的服务组,表示一组服务。

我们打开ssh.service可以看到WantedBy=multi-user.target,指定了ssh.service所在的服务组为multi-user.target多用户服务组。

[Unit]
Description=OpenBSD Secure Shell server
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

[Install]
WantedBy=multi-user.target
Alias=sshd.service

当我们执行systemctl enable sshd.service命令时,sshd.service的一个符号链接,就会放在/etc/systemd/system目录下面的multi-user.target.wants子目录之中;这里讲解了(1.2 单元的管理)中的疑点。

我们可以通过执行systemctl list-dependencies multi-user.target来查看当前multi-user.target服务组下的服务,

systemd PID 配置 systemd详解_字段_03

systemd的target

Systemd 目标

注释

runlevel0.target, poweroff.target

中断系统(halt)

runlevel1.target, rescue.target

单用户模式

runlevel2.target, runlevel4.target, multi-user.target

用户自定义启动级别,通常识别为级别3。

runlevel3.target, multi-user.target

多用户,无图形界面。用户可以通过终端或网络登录。

runlevel5.target, graphical.target

多用户,图形界面。继承级别3的服务,并启动图形界面服务。

runlevel6.target, reboot.target

重启

emergency.target

急救模式(Emergency shell)

Systemd默认的启动Target为multi-user.target,我们也可以通过指令去查看当前启动的target

$systemctl get-default
multi-user.target

一般来说,常用的 Target 有两个:一个是multi-user.target,表示多用户命令行状态;另一个是graphical.target,表示图形用户状态,它依赖于multi-user.target

3.2.1 target 的配置文件

打开multi-user.target文件,如下:

#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=Multi-User System
Documentation=man:systemd.special(7)
Requires=basic.target
Conflicts=rescue.service rescue.target
After=basic.target rescue.service rescue.target
AllowIsolate=yes

可以看到target 配置文件里面没有启动命令,主要字段如下:

  • Requires字段:要求basic.target一起运行。
  • Conflicts字段:冲突字段。如果rescue.servicerescue.target正在运行,multi-user.target就不能运行,反之亦然。
  • After:表示multi-user.targetbasic.targetrescue.servicerescue.target之后启动,如果它们有启动的话。
  • AllowIsolate:允许使用systemctl isolate命令切换到multi-user.target

修改配置文件以后,需要重新加载配置文件,然后重新启动相关服务。

# 重新加载配置文件
$ sudo systemctl daemon-reload

# 重启相关服务
$ sudo systemctl restart foobar