【linux】使用systemctl start xxx启动自己的程序|开机启动|守护进程

目录

即看即用

详细说明

systemctl 的用途

用法

例子:以管理我们的程序verdaccio为例

开机启动

设置程序开机启动、关闭、启用/禁用服务以vsftpd为例

三个部分[Unit]、[Service]、[Install]配置说明

systemctl或service启动服务日志

systemctl和service的区别


即看即用

  • 在 /lib/systemd/system/  (or /usr/lib/systemd/system)下创建一个单元文件,比如:vsftpd.service/vsftpd.target
  • 写配置文件
[Unit]
Description=vsftpd lightweight npm proxy registry

[Service]
Type=simple
Restart=on-failure
User=verdaccio
ExecStart=/usr/bin/vsftpd--config /etc/vsftpd/config.yaml

[Install]
WantedBy=multi-user.target
  • 添加启动权限

# chmod +x vsftpd.service

启动服务:systemctl start vsftpd.service

关闭服务:systemctl stop vsftpd.service

重启服务:systemctl restart vsftpd.service

显示服务的状态:systemctl status vsftpd.service

在开机时启用服务:systemctl enable vsftpd.service

在开机时禁用服务:systemctl disable vsftpd.service

查看服务是否开机启动:systemctl is-enabled vsftpd.service

查看已启动的服务列表:systemctl list-unit-files|grep enabled

查看启动失败的服务列表:systemctl --failed

详细说明

systemd和systemctl详解 |https://www.liuvv.com/p/c9c96ac3.html

systemctl 的用途

       有时我们将自定义程序注册为systemd service 进程管理,交由系统管理,可以方便启动停止,亦可以实现服务异常退出重启,开机自启动。 减少自定义程序服务管理的时间消耗。

历史上,Linux 的启动一直采用init进程。 Systemd 设计目标是,为系统的启动和管理提供一套完整的解决方案。

用法

1、新增单元文件如a.service(添chmod +x   a.service),按要求里面写start、stop等对应要执行的操作。

2、单元文件放到/usr/lib/systemd/system (or /lib/systemd/system)文件夹下

3、可以用systemctl管理执行了:systemctl stop xxx,systemctl start xxx

(分*.service、*.Target、*.Device等12类单元文件,见systemd和systemctl详解 | https://www.liuvv.com/p/c9c96ac3.html)

例子:以管理我们的程序verdaccio为例


第1步:创建一个systemd unit 配置文件,比如:verdaccio.service,一般放在 /lib/systemd/system/ (or /usr/lib/systemd/system)下
内容如下:

[Unit]
Description=Verdaccio lightweight npm proxy registry

[Service]
Type=simple
Restart=on-failure
User=verdaccio
ExecStart=/usr/bin/verdaccio --config /etc/verdaccio/config.yaml

[Install]
WantedBy=multi-user.target

第2步:添加tomcat.service文件的启动权限

# chmod +x tomcat.service


第3步:使用

开机自启动:
systemctl enable verdaccio.service

立即启动:
systemctl start verdaccio.service

重新启动:
systemctl restart verdaccio.service

运行状态:
systemctl status verdaccio.service

原文链接:https://www.icode9.com/content-3-952939.html

开机启动

systemctl脚本存放在:/usr/lib/systemd/,有系统(system)和用户(user)之
分,需要开机不登陆就能运行的程序,存在系统服务里,即:/usr/lib/systemd/system目录下.
每一个服务以.service结尾,一般会分为3部分:[Unit]、[Service]和[Install]

[Unit]
部分主要是对这个服务的说明,内容包括Description和After,Description 用于描述服务,
After用于描述服务类别

Description=test      # 简单描述服务
  After=network.target  # 描述服务类别,表示本服务需要在network服务启动后在启动
  Before=xxx.service    # 表示需要在某些服务启动之前启动,After和Before字段只涉及启动顺序,不涉及依赖关系。

[Service]部分是服务的关键,是服务的一些具体运行参数的设置.

Type=forking     # 表示后台运行模式。
 User=user        # 设置服务运行的用户
 Group=user       # 设置服务运行的用户组
 KillMode=control-group   # 定义systemd如何停止服务
 PIDFile=/usr/local/test/test.pid    # 存放PID的绝对路径
 Restart=no        # 定义服务进程退出后,systemd的重启方式,默认是不重启
 ExecStart=/usr/local/test/bin/startup.sh    # 服务启动命令,命令需要绝对路径
 ExecReload        #为重启命令
 ExecStop          #为停止命令,
 PrivateTmp=true   # 表示给服务分配独立的临时空间

[Service]
注意:[Service]部分的启动、重启、停止命令全部要求使用绝对路径,使用相对路径则会报
错!

[Install]
[Install]部分是服务安装的相关设置,可设置为多用户的
首先,使用systemctl start [ 服务名(也是文件名) ] 可测试服务是否可以成功运行,如果不能运行 则可以使用systemctl status [ 服务名(也是文件名) ]查看错误信息和其他服务信息,然后根据报
错进行修改,直到可以start,如果不放心还可以测试restart和stop命令。
接着,只要使用systemctl enable xxxxx就可以将所编写的服务添加至开机启动即可。

[Unit]
Description=springboot webapp
After=springboot-webapp.service

[Service]
WorkingDirectory=/home/hlooc/app/springboot-webapp
Type=forking
Environment="JAVA_HOME=/home/hlooc/app/jdk1.8.0_202"
Environment="PATH=/home/hlooc/app/jdk1.8.0_202/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/hlooc/.local/bin:/home/hlooc/bin"
Environment="CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar"
User=hlooc
Group=hlooc
PIDFile=/home/hlooc/app/springboot-webapp/upp.pid
ExecStart=/home/hlooc/app/springboot-webapp/start.sh
ExecStop=/home/hlooc/app/springboot-webapp/stop.sh
PrivateTmp=true

[Install]
WantedBy=multi-user.target

服务操作

添加可执行权限:

chmod 754 /usr/lib/systemd/system/springboot-webapp.service

设置为开机自启动:

systemctl enable springboot-webapp.service常用指令(以springboot-webapp服务为例):

启动某服务

systemctl start springboot-webapp.service停止某服务

systemctl stop springboot-webapp.service重启某服务

service springboot-webapp restart
systemctl restart springboot-webapp.service使某服务自动启动(如springboot-webapp服务)

systemctl enable springboot-webapp.service使某服务不自动启动

systemctl disable springboot-webapp.service检查服务状态

systemctl status springboot-webapp.service (服务详细信息)
systemctl is-active springboot-webapp.service(仅显示是否Active)显示所有已启动的服务

systemctl list-units --type=service
-----------------------------------
Linux使用systemctl自启动服务
https://blog.51cto.com/u_15127670/4380043

设置程序开机启动、关闭、启用/禁用服务以vsftpd为例

启动服务:systemctl start vsftpd.service

关闭服务:systemctl stop vsftpd.service

重启服务:systemctl restart vsftpd.service

显示服务的状态:systemctl status vsftpd.service

在开机时启用服务:systemctl enable vsftpd.service

在开机时禁用服务:systemctl disable vsftpd.service

查看服务是否开机启动:systemctl is-enabled vsftpd.service

查看已启动的服务列表:systemctl list-unit-files|grep enabled

查看启动失败的服务列表:systemctl --failed

systemctl设置程序开机启动、关闭、启用/禁用服务以vsftpd为例

原文:https://www.cnblogs.com/Li-JT/p/14018659.html

.service 参数详解(更具体的可参考http://www.jinbuguo.com/systemd/systemd.service.html)

三个部分[Unit]、[Service]、[Install]配置说明

service文件中包含三个部分,分别是:[Unit]、[Service]、[Install]

[Unit] 部分
Description 一段描述这个 Unit 文件的文字,通常只是简短的一句话。
Documentation 指定服务的文档,可以是一个或多个文档的URL路径。
Requires 依赖的其他 Unit 列表,列在其中的 Unit 模块会在这个服务启动的同时被启动,并且如果其中有任意一个服务启动失败,这个服务也会被终止
After 与 Requires 相似,但会在后面列出的所有模块全部启动完成以后,才会启动当前的服务。
Before 与 After 相反,在启动指定的任一个模块之前,都会首先确保当前服务已经运行。
Wants 与 Requires 相似,但只是在被配置的这个 Unit 启动时,触发启动列出的每个 Unit 模块,而不去考虑这些模块启动是否成功。
Conflicts 与这个模块有冲突的模块,如果列出模块中有已经在运行的,这个服务就不能启动,反之亦然。
OnFailure 当这个模块启动失败时,就自动启动列出的每个模块。
PartOf

与 Requires= 类似, 不同之处在于:仅作用于单元的停止或重启。

其含义是,当停止或重启这里列出的某个单元时, 也会同时停止或重启该单元自身。 注意,这个依赖是单向的, 该单元自身的停止或重启并不影响这里列出的单元。

如果 a.service 中包含了 PartOf=b.service ,那么这个依赖关系将在 b.service 的属性列表中显示为 ConsistsOf=a.service 。 也就是说,不能直接设置 ConsistsOf= 依赖。

@

在 Linux 上配置过 open 的同学应该都看到过一些 `[email protected]` 这样的例子:

若需在系统启动时自动启动 OpenVPN,对服务器端与客户端,都可以采用在对应机器上 启用 [email protected] 的方式配置。例如,如果客户端配置文件是 /etc/open/client.conf,则服务名称应为 [email protected]。或者,如果服务器端配置文件是 /etc/open/server.conf,则服务名称应为 [email protected]

(systemd 常用操作及配置 | Yiran's Blog)

[Service] 部分
Type

设置进程的启动类型。必须设为 simpleexecforkingoneshotdbusnotifyidle 之一,默认的 simple 类型可以适应于绝大多数的场景,因此一般可以忽略这个参数的配置。而如果服务程序启动后会通过 fork 系统调用创建子进程,然后关闭应用程序本身进程的情况,则应该将 Type 的值设置为 forking,否则 systemd 将不会跟踪子进程的行为,而认为服务已经退出。

其他类型说明见文章附录。

Environment 为服务添加环境变量
EnvironmentFile 指定加载一个包含服务所需的环境变量列表的文件,文件中的每一行都是一个环境变量的定义。
ExecStart 这个参数是几乎每个 .service 文件都会有的,指定服务启动的主要命令,在每个配置文件中只能使用一次。需要使用绝对路径
ExecStartPre 指定在启动执行 ExecStart 的命令前的准备工作,可以有多个,所有命令会按照文件中书写的顺序依次被执行。
ExecStartPost 指定在启动执行 ExecStart 的命令后的收尾工作,也可以有多个。
ExecStop 停止服务所需要执行的主要命令。需要使用绝对路径
ExecStopPost 指定在 ExecStop 命令执行后的收尾工作,也可以有多个。
ExecReload 重新加载服务所需执行的主要命令。需要使用绝对路径
Restart 这个值用于指定在什么情况下需要重启服务进程。常用的值有 no,on-success,on-failure,on-abnormal,on-abort 和 always。默认值为 no,即不会自动重启服务。这些不同的值分别表示了在哪些情况下,服务会被重新启动
RestartSec 如果服务需要被重启,这个参数的值为服务被重启前的等待秒数。
Nice 服务的进程优先级,值越小优先级越高,默认为0。-20为最高优先级,19为最低优先级。
WorkingDirectory 指定服务的工作目录。
RootDirectory 指定服务进程的根目录( / 目录),如果配置了这个参数后,服务将无法访问指定目录以外的任何文件。
User 指定运行服务的用户,会影响服务对本地文件系统的访问权限。
Group 指定运行服务的用户组,会影响服务对本地文件系统的访问权限。
PrivateTmp

# 设为 true表示在进程的文件系统名字空间中挂载私有的 /tmp 与 /var/tmp 目录, 也就是不与名字空间外的其他进程共享临时目录。 这样做会增加进程的临时文件安全性,但同时也让进程之间无法通过 /tmp 或 /var/tmp 目录进行通信。

# 适用于web系统服务,不适用于mysql之类的数据库用户服务,数据库用户服务设为false。 PrivateTmp=true

[Install] 部分
WantedBy

依赖当前服务的模块。

“WantedBy=multi-user.target” 表明当系统以多用户方式(默认的运行级别)启动时,这个服务需要被自动运行。当然还需要 systemctl enable 激活这个服务以后自动运行才会生效

RequiredBy 依赖当前服务的模块。
Also 当这个服务被 enable/disable 时,将自动 enable/disable 后面列出的每个模块。

其中[Service] 部分Restart里面对应命令对应的情况如下图

【linux】使用systemctl start xxx启动自己的程序|开机启动|守护进程_第1张图片


作者:Hmcf
链接:https://www.jianshu.com/p/bc17d3162a48

systemctl或service启动服务日志

最近在使用 systemctl start xxx 发现启动失败了,可是并没有错误日志输出,这让人很抓狂!

最近在启动 minio 集群的时候,发现启动失败,可是并没有详细的错误日志输出,不好排查。

发现通过命令 journalctl -u 服务名 可以查找到指定服务的启动日志。

>> 简介

journalctl 用来查询 systemd-journald 服务收集到的日志。systemd-journald 服务是 systemd init 系统提供的收集系统日志的服务。在 Systemd 出现之前,Linux 系统及各应用的日志都是分别管理的,Systemd 开始统一管理了所有 Unit 的启动日志,这样带来的好处就是可以只用一个 journalctl 命令,查看所有内核和应用日志。

>> 常用命令

查看某个 Unit 的日志

journalctl -u minio.service
1
>> 结果

Aug 24 07:45:04 localhost.localdomain run.sh[6822]: /home/tools/minio/run.sh: line 5: /home/tools/minio: Is a directo
Aug 24 07:45:04 localhost.localdomain systemd[1]: minio.service: main process exited, code=exited, status=126/n/a
Aug 24 07:45:04 localhost.localdomain systemd[1]: Unit minio.service entered failed state.
Aug 24 07:45:04 localhost.localdomain systemd[1]: minio.service failed.
Aug 24 07:45:09 localhost.localdomain systemd[1]: minio.service holdoff time over, scheduling restart.
Aug 24 07:45:09 localhost.localdomain systemd[1]: Started Minio service.
Aug 24 07:45:09 localhost.localdomain systemd[1]: Starting Minio service...
Aug 24 07:45:09 localhost.localdomain run.sh[6825]: /home/tools/minio/run.sh: line 5: /home/tools/minio: Is a directo
Aug 24 07:45:09 localhost.localdomain systemd[1]: minio.service: main process exited, code=exited, status=126/n/a
Aug 24 07:45:09 localhost.localdomain systemd[1]: Unit minio.service entered failed state.
Aug 24 07:45:09 localhost.localdomain systemd[1]: minio.service failed.
Aug 24 07:45:14 localhost.localdomain systemd[1]: minio.service holdoff time over, scheduling restart.
原文链接:https://blog.csdn.net/mikelv01/article/details/108201676

systemctl和service的区别

区别

service是一个脚本命令,是去/etc/init.d目录下执行相关程序来启动服务。

 systemd是Linux系统最新的初始化系统(init),作用是提高系统的启动速度,尽可能启动较少的进程,尽可能更多进程并发启动。systemd对应的进程管理命令是systemctl,兼容service,也会去/etc/init.d目录下,查看执行相关程序。

传统的init启动模式里面,有 RunLevel (运行级别),但是Systemd 是使用 Target 来管理。


链接:https://www.jianshu.com/p/bb2d68fb2987

为什么systemctl取代service

Linux 的启动一直采用init进程。串行启动时间长,启动脚本复杂。Systemd取代了initd,成为系统的第一个进程(PID= 1),提高系统的启动速度,尽可能启动较少的进程,尽可能更多进程并发启动。管理系统的服务、系统资源,不同的资源统称为 Unit。

目录

    service服务配置文件存放目录/etc/init.d/。

    Systemd 默认从目录/etc/systemd/system/读取配置文件。但是,里面存放的大部分文件都是符号链接,指向目录/usr/lib/systemd/system/,真正的配置文件存放在那个目录。systemctl enable命令用于在上面两个目录之间,建立符号链接关系。


链接:https://www.jianshu.com/p/bb2d68fb2987

service

方法按照格式新建个service放置于/etc/init.d文件中,然后使用chkconfig或者update-rc.d来设置运行级别开机启动。

RunLevel(运行级别)的概念

    传统的init启动模式里面,有 RunLevel ,但是Systemd 是使用 Target 来管理。

    默认的 RunLevel,在/etc/inittab文件设置,Target 是/etc/systemd/system/default.target,通常符号链接到graphical.target(图形界面)或者multi-user.target(多用户命令行)。

【linux】使用systemctl start xxx启动自己的程序|开机启动|守护进程_第2张图片

Target 与 传统 RunLevel 的对应关系:

【linux】使用systemctl start xxx启动自己的程序|开机启动|守护进程_第3张图片

Linux系统有7个运行级别(runlevel)
运行级别0:系统停机状态,系统默认运行级别不能设为0,否则不能正常启动
运行级别1:单用户工作状态,root权限,用于系统维护,禁止远程登陆
运行级别2:多用户状态(没有NFS)
运行级别3:完全的多用户状态(有NFS),登陆后进入控制台命令行模式
运行级别4:系统未使用,保留
运行级别5:X11控制台,登陆后进入图形GUI模式
运行级别6:系统正常关闭并重启,默认运行级别不能设为6,否则不能正常启动

注:重点关注3和5就行了。

运行级别的原理:
1。在目录/etc/rc.d/init.d下有许多服务器脚本程序,一般称为服务(service)
2。在/etc/rc.d下有7个名为rcN.d的目录,对应系统的7个运行级别
3。rcN.d目录下都是一些符号链接文件,这些链接文件都指向init.d目录下的service脚本文件,命名规则为K+nn+服务名或S+nn+服务名,其中nn为两位数字。
4。系统会根据指定的运行级别进入对应的rcN.d目录,并按照文件名顺序检索目录下的链接文件
对于以K开头的文件,系统将终止对应的服务
对于以S开头的文件,系统将启动对应的服务
5。查看运行级别用:runlevel
6。进入其它运行级别用:init N
7。另外init0为关机,init 6为重启系统
原文链接:https://blog.csdn.net/wanglei9876/article/details/50338279

此时/etc/inittab文件已经不再使用了,查看里面其实都是注释与说明:

【linux】使用systemctl start xxx启动自己的程序|开机启动|守护进程_第4张图片

查看默认运行级别: systemctl get-default

修改用户级别为多用户命令行:sudo systemctlset-default multi-user.target。即为删除原有链接,新建multi-user.target新链接。可用通过命令systemctl list-dependencies multi-user.target查看 multi-user.target启动时的默认 Target。

本章节摘自:https://www.jianshu.com/p/bb2d68fb2987

====================================================

好文备份:systemd和systemctl详解 | Levonfly's Blog

systemd和systemctl详解 | Levonfly's Blog

1. Systemd

1.1 前言

历史上,Linux 的启动一直采用init进程。 Systemd 设计目标是,为系统的启动和管理提供一套完整的解决方案。

Systemd 是一系列工具的集合,其作用也远远不仅是启动操作系统,它还接管了后台服务、结束、状态查询,以及日志归档、设备管理、电源管理、定时任务等许多职责,并支持通过特定事件(如插入特定 USB 设备)和特定端口数据触发的 On-demand(按需)任务。

Systemd 的后台服务还有一个特殊的身份——它是系统中 PID 值为 1 的进程。

1.2 特点

  • 更少的进程

    Systemd 提供了 服务按需启动 的能力,使得特定的服务只有在真定被请求时才启动。

  • 允许更多的进程并行启动

    在 SysV-init 时代,将每个服务项目编号依次执行启动脚本。Ubuntu 的 Upstart 解决了没有直接依赖的启动之间的并行启动。而 Systemd 通过 Socket 缓存、DBus 缓存和建立临时挂载点等方法进一步解决了启动进程之间的依赖,做到了所有系统服务并发启动。对于用户自定义的服务,Systemd 允许配置其启动依赖项目,从而确保服务按必要的顺序运行。

  • 使用 CGroup 跟踪和管理进程的生命周期

    在 Systemd 之间的主流应用管理服务都是使用 进程树 来跟踪应用的继承关系的,而进程的父子关系很容易通过 两次 fork 的方法脱离。

    而 Systemd 则提供通过 CGroup 跟踪进程关系,引补了这个缺漏。通过 CGroup 不仅能够实现服务之间访问隔离,限制特定应用程序对系统资源的访问配额,还能更精确地管理服务的生命周期。

  • 统一管理服务日志

    Systemd 是一系列工具的集合, 包括了一个专用的系统日志管理服务:Journald。这个服务的设计初衷是克服现有 Syslog 服务的日志内容易伪造和日志格式不统一等缺点,Journald 用 二进制格式 保存所有的日志信息,因而日志内容很难被手工伪造。Journald 还提供了一个 journalctl 命令来查看日志信息,这样就使得不同服务输出的日志具有相同的排版格式, 便于数据的二次处理。

1.3 Unit 和 Target

Unit 是 Systemd 管理系统资源的基本单元,可以认为每个系统资源就是一个 Unit,并使用一个 Unit 文件定义。在 Unit 文件中需要包含相应服务的描述、属性以及需要运行的命令。

Target 是 Systemd 中用于指定系统资源启动组的方式,相当于 SysV-init 中的运行级别。

简单说,Target 就是一个 Unit 组,包含许多相关的 Unit 。启动某个 Target 的时候,Systemd 就会启动里面所有的 Unit。从这个意义上说,Target 这个概念类似于”状态点”,启动某个 Target 就好比启动到某种状态。

1.4 Systemd 目录

Unit 文件按照 Systemd 约定,应该被放置指定的三个系统目录之一中。这三个目录是有优先级的,如下所示,越靠上的优先级越高。因此,在三个目录中有同名文件的时候,只有优先级最高的目录里的那个文件会被使用。

  • /etc/systemd/system:系统或用户自定义的配置文件
  • /run/systemd/system:软件运行时生成的配置文件
  • /usr/lib/systemd/system:系统或第三方软件安装时添加的配置文件。

Systemd 默认从目录 /etc/systemd/system/ 读取配置文件。但是,里面存放的大部分文件都是符号链接,指向目录 /usr/lib/systemd/system/,真正的配置文件存放在那个目录。

2. Unit

Systemd 可以管理所有系统资源:将系统资源划分为12类。将每个系统资源称为一个 Unit。

Unit 是 Systemd 管理系统资源的基本单位。使用一个 Unit File 作为 Unit 的单元文件,Systemd 通过单元文件控制 Unit 的启动。

例如,MySQL服务被 Systemd 视为一个 Unit,使用一个 mysql.service 作为启动配置文件

2.1 Unit File

Systemd 将系统资源划分为12类,对应12种类型的单元文件

系统资源类型 单元文件扩展名 单元文件描述 备注
Service .service 封装守护进程的启动、停止、重启和重载操作,是最常见的一种 Unit 文件 系统服务
Target .target 定义 target 信息及依赖关系,一般仅包含 Unit 段 多个 Unit 构成的一个组
Device .device 对于 /dev 目录下的硬件设备,主要用于定义设备之间的依赖关系 硬件设备
Mount .mount 定义文件系统的挂载点,可以替代过去的 /etc/fstab 配置文件 文件系统的挂载点
Automount .automount 用于控制自动挂载文件系统,相当于 SysV-init 的 autofs 服务 自动挂载点
Path .path 用于监控指定目录或文件的变化,并触发其它 Unit 运行 文件或路径
Scope .scope 这种 Unit 文件不是用户创建的,而是 Systemd 运行时产生的,描述一些系统服务的分组信息 不是由 Systemd 启动的外部进程
Slice .slice 用于表示一个 CGroup 的树 进程组
Snapshot .snapshot 用于表示一个由 systemctl snapshot 命令创建的 Systemd Units 运行状态快照,可以切回某个快照 Systemd 快照,可以切回某个快照
Socket .socket 监控来自于系统或网络的数据消息 进程间通信的 socket
Swap .swap 定义一个用户做虚拟内存的交换分区 swap 文件
Timer .timer 用于配置在特定时间触发的任务,替代了 Crontab 的功能 定时器

对于操作单元文件的命令,如果缺省扩展名,则默认.service扩展名

2.2 语法

先看一个示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 
[Unit]
Description=Hello World
After=docker.service
Requires=docker.service
[Service]
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker kill busybox1
ExecStartPre=-/usr/bin/docker rm busybox1
ExecStartPre=/usr/bin/docker pull busybox
ExecStart=/usr/bin/docker run --name busybox1 busybox /bin/ sh -c "while true; do echo Hello World; sleep 1; done"
ExecStop="/usr/bin/docker stop busybox1"
ExecStopPost="/usr/bin/docker rm busybox1"
[Install]
WantedBy=multi-user.target
 

Unit 文件可以分为三个配置区段:

  • Unit 段:所有 Unit 文件通用,用来定义 Unit 的元数据,以及配置与其他 Unit 的关系
  • Service 段:服务(Service)类型的 Unit 文件(后缀为 .service)特有的,用于定义服务的具体管理和执行动作
  • Install 段:所有 Unit 文件通用,用来定义如何启动,以及是否开机启动

Unit 和 Install 段:所有 Unit 文件通用,用于配置服务(或其它系统资源)的描述、依赖和随系统启动的方式

Service 段:服务(Service)类型的 Unit 文件(后缀为 .service)特有的,用于定义服务的具体管理和操作方法

单元文件中的区段名和字段名大小写敏感, 每个区段内都是一些等号连接的键值对(键值对的等号两侧不能有空格)

2.3 Unit 段

Unit 通常是配置文件的第一个区块,用来定义 Unit 的元数据,以及配置与其他 Unit 的关系。

  • Description:当前服务的简单描述
  • Documentation:文档地址,可以是一个或多个文档的 URL 路径
  • Requires:与其它 Unit 的强依赖关系,如果其中任意一个 Unit 启动失败或异常退出,当前 Unit 也会被退出
  • Wants:与其它 Unit 的弱依赖关系,如果其中任意一个 Unit 启动失败或异常退出,不影响当前 Unit 继续执行
  • After:该字段指定的 Unit 全部启动完成以后,才会启动当前 Unit
  • Before:该字段指定的 Unit 必须在当前 Unit 启动完成之后再启动
  • Binds To:与 Requires 相似,该字段指定的 Unit 如果退出,会导致当前 Unit 停止运行
  • Part Of:一个 Bind To 作用的子集,仅在列出的 Unit 失败或重启时,终止或重启当前 Unit,而不会随列出Unit 的启动而启动
  • OnFailure:当这个模板启动失败时,就会自动启动列出的每个模块
  • Conflicts:与这个模块有冲突的模块,如果列出的模块中有已经在运行的,这个服务就不能启动,反之亦然

Description=   :一些描述,显示给用户界面看的,可以是任何字符串,一般是关于服务的说明。

        Documentation=  :指定参考文档的列表,以空格分开的 URI 形式,如http://, https://, file:, info:, man:,这是有顺序的,最好是先解释这个服务的目的是什么,然后是它是如何配置的,再然后是其它文件,这个选项可以多次指定,会将多行的合并,如果指定了一个空的,那么会重置此项,前的配置不在起作用。

        Requires=   :指定此服务依赖的其它服务,如果本服务被激活,那么 Requires 后面的服务也会被激活,反之,如果 Requires 后面的服务被停止或无法启动,则本服务也会停止。这个选项可以指定多次,那么就要求所有指定的服务都被激活。需要注意的是这个选项不影响启动或停止的顺序,启动顺序使用单句的 After= 和 Before= 来配置。例如,如果 foo.service 依赖  bar.serivce,但是只配置了 Requires= 而没有 After= 或 Before=,那么 foo.service 启动时会同时激活 foo.service 和 bar.service。通常使用 Wants= 代替 Requires= 是更好的选择,因为系统会更好的处理服务失败的情况。注意,这种依赖关系,也可以在文件之外来处理,即使用 .requires/ 目录,可以参看上面的说明。

        RequiresOverridable=     :类似上面的 Requires= ,不过这种情况下,只要用户明确要求它启动,才会影响到被依赖的服务,不然服务出错什么的,不会影响被依赖服务的启动。

        Requisite=RequisiteOverridable=  :分别类似上面的两个,不过如果是这个指定服务没有启动,被依赖的服务会不启动,立即失败。

        Wants=    :相对弱化的 Requires= ,这里列出的服务会被启动,但如果无法启动或无法添加到事务处理,并不影响本服务做为一个整体的启动。这是推荐的两个服务关联的方式。这种依赖也可以配置文件外,通过 .wants/ 目录添加,具体可以看上面的说明。

        BindsTo= :和 Requires= 很像,但是这种情况,如果他后面列出的服务停止运行或崩溃之类的,本服务也会同时停止。

        PartOf=  :又一个类似 Requires= 的选项,但是限制在停止或重启动服务,如果这里列出的服务被停止或重启动,那么本服务也会停止或重启动,注意这个依赖是意向,即本服务停止或重启动,不会影响到这里列出服务的运行状态。

        Conflicts= :配置一个依赖冲突,如果配置了些项,那么,当一个服务启动时,或停止此处列出的服务,反过来,如果这里列出的服务启动,那么本服务就会停止,即后启动的才起作用。注意,此设置和 After= 和 Before= 是互相独立的。如果服务 A 和 B 冲突,且在 B 启动的时候同时启动,那么有可能会启动失败(两都都是必需的)或修改以修复它(两者之一或两都都不是必需的),后一种情况,会将不需要的依赖删除,或停止冲突。

        Before=After=  :配置服务间的启动顺序,比如一个 foo.service 包含了一行 Before=bar.service,那么当他们同时启动时,bar.service 会等待 foo.service 启动完成后才启动。注意这个设置和 Requires= 的相互独立的,同时包含 After= 和 Requires= 也是常见的。此选项可以指定一次以上,这时是按顺序全部启动。

        OnFailure=       :列出一个或更多的服务,当本服务启动状态是 failed 的时候,激活这些服务。

        PropagatesReloadTo=ReloadPropagatedFrom=  :这两个是列出一些服务,当其它服务 reload 时同时 reload 这个服务,或者反之。

        RequiresMountsFor=  :用空格分开的绝对路径列表,是 Requires= 和 After= 添加的依赖中的 mount 文件需要访问的指定的路径。

        OnFailureIsolate=  :是一个布尔值,如果是真,那么 OnFailure= 后面的服务会进入隔离模式,即所有不是它依赖的服务都会停止。如果只设置一个服务,可以放在 OnFailure= 后,默认值是假。

        IgnoreOnIsolate=    :一个布尔值.如果是真则当隔离其它服务时本服务不会停止(不明白隔离是什么意思,大概在后面)。默认是假。

        IgnoreOnSnapshot=  :一个布尔值.如果是真则本服务不包含快照(snapshots)。对 device 和 snapshot 服务默认为真,其它服务默认为假。

        StopWhenUnneeded=  :一个布尔值。如果是真则当本服务不使用时会停止。 注意,为了尽量减少 systemd 的工作,默认情况下是不会停止不使用的服务的,除非和其它服务冲突,或用户明确要求停止。如果设置了这个选项,那么如果没有其它活动的服务需要此服务,它会自动停止。默认值是假。

        RefuseManualStart=RefuseManualStop=  :布尔值。如果设为真值,则此服务只能间接的激活或停止。这种情况下,用户直接启动或停止此服务会被拒绝,只有做为其它的服务依赖关系,由其它服务进行启动或停止才可以。这主要是为了停止用户误操作。默认值是假。

        AllowIsolate=      :布尔值。如果是真值,则此服务可以使用 systemctl isolate 命令进行操作。否则会拒绝此操作。最好的办法是不要动这处选项,除非目标服务的行为类似于 SysV 启动系统中的 runlevels。只是一种预防措施,避免系统无法使用的状态。默认值是假。

        DefaultDependencies=  :布尔值。如果是真(默认值),一些本服务默认的依赖会隐式的建立,具体是哪些依赖,则于服务的类型决定。比如,对于普通的服务(.service类型),它会确保在系统基本服务启动后才启动本服务,会在系统关机前确保本服务已关闭。一般来说,只有早期开机服务和后期的关机服务,才需要把这个设成假。强烈对大多数普通服务,让这个选项启用即可。如果设成假,也不会禁用所有的隐式依赖,只是禁用那些非必要的。

        JobTimeoutSec=  :当一个客户端等待本服务的某个 Job 完成时,所指定的超时时间。如果达到了限制的时间,此 Job 会取消运行,但服务不会更改状态,包括进入“failed”状态。除了设备服务(即.device类型),其它的默认值是0(即没有超时设置)。注意,这个是独立于特定服务所设置的超时设置的(比如对 .service 类型所设置的 Timeout=),它对服务本身没有影响,但特定服务的设置是有影响的(能用来更改服务状态)。

ConditionPathExists=ConditionPathExistsGlob=ConditionPathIsDirectory=ConditionPathIsSymbolicLink=ConditionPathIsMountPoint=ConditionPathIsReadWrite=ConditionDirectoryNotEmpty=,ConditionFileNotEmpty=ConditionFileIsExecutable=ConditionKernelCommandLine=ConditionVirtualization=ConditionSecurity=ConditionCapability=ConditionHost=ConditionACPower=,ConditionNull=  :这是一组类似的东西。检测特定的条件是不是真值,如果不是真值,服务会略过启动,但是它依赖的服务还是会正常运行的。这个条件测试失败不会让服务进入失败状态。条件是在服务开始运行时检查的。

  ConditionPathExists= 是指定在服务启动时检查指定文件的存在状态。如果指定的绝对路径名不存在,这个条件的结果就是失败。如果绝对路径的带有!前缀,则条件反转,即只有路径不存在时服务才启动。

  ConditionPathExistsGlob= 类似上面的选项,但支持通配符。

  ConditionPathIsDirectory= 判断指定路径是不是目录。

  ConditionPathIsSymbolicLink= 判断指定路径是不是链接。

  ConditionPathIsMountPoint= 判断指定路径是不是一个挂载点。

  ConditionPathIsReadWrite= 多年指定路径是否可读写(即不是做为只读系统挂载的)

  ConditionDirectoryNotEmpty= 判断指定目录是否存在且不为空。

  ConditionFileNotEmpty= 判断指定文件是否是常规文件且不为空(即大小不是0)。

  ConditionFileIsExecutable= 判断指定文件是否是常规文件且可执行。

  类似的,ConditionKernelCommandLine=是判断有没有指定的内核命令行启动参数(或带有!反之),这个参数必须是一个单词或用=分开的两个单词,前一种情况下,会寻找内核参数是否有此单词或是赋值的左边。后一种情况则必须是赋值的左右同时符合。

  ConditionVirtualization= 是判断是不是在虚拟化环境下执行的服务。这可以是个布尔值以判断是不是任意的虚拟化环境,或者下列的字符串之一: qemu, kvm, vmware, microsoft, oracle, xen, bochs, chroot, openvz, lxc, lxc-libvirt, systemd-nspawn,以判断是不是特定的虚拟化环境,多重嵌套的虚拟化环境,只判断最后一层。可以使用!进行反转判断。

  ConditionSecurity= 是判断系统是否启用了安全环境,当前仅能识别selinuxapparmor, 和 smack。可以使用!进行反转判断。

  ConditionCapability= 是判断服务管理器绑定的 capability 是否存在。(可以查看其它部分的详细信息。)设置为 capability 的名字,比如 CAP_MKNOD。可以通过在前面加!反转判断。

  ConditionHost= 是判断主机名 (hostname)或机器ID(machine ID)是否匹配。可以加!反转。

  ConditionACPower= 是判断机器是否在使用交流电源。如果设成 true,而只有至少连接一个交流电源时结果才为真,反过来,设成 false,则不连接所有交流电源时才为真。

  最后,ConditionNull= 是一个常量性质的判断条件,它应该是布尔值,如果设成 false ,则条件永远失败,反过来则永远成立。

  如果指定多个条件,则所有条件都需要成立(即条件之间是 AND 的关系)。条件前面可以加上 | 符号,这时条件变成一个触发条件,服务定义了触发条件,那么在满足其它非触发条件和这个触发条件的情况下,服务会至少执行一次。同时指定|和!前缀时,先处理|,后处理!。除了ConditionPathIsSymbolicLink=,其它条件均跟随链接。如果这些条件指定为空,则相当于重置,前面的任何设置都不再起作用。

        SourcePath=  :这个服务生成的配置文件所在的路径,这主要是用在生成工具从外部配置文件的格式转换到本地服务的配置格式中。因此,对一般的服务不要使用此选项。

服务文件还可能包含一个 [Install] 段,这个段的内容服务的安装信息。它不在 systemd 的运行期间使用。只在使用 systemctl enable 和 systemctl disable 命令启用/禁用服务时有用。

        Alias=  :在安装使用应该使用的额外名字(即别名)。名字必须和服务本身有同样的后缀(即同样的类型)。这个选项可以指定多次,所有的名字都起作用,当执行 systemctl enable 命令时,会建立相当的链接。

        WantedBy=RequiredBy= :在 .wants/ 或 .requires/ 子目录中为服务建立相应的链接。这样做的效果是当列表中的服务启动,本服务也会启动。 在  bar.service 中的 WantedBy=foo.service  和 Alias=foo.service.wants/bar.service 基本是一个意思。

        Also=  :当此服务安装时同时需要安装的附加服务。 如果用户请求安装的服务中配置了此项,则 systemctl enable 命令执行时会自动安装本项所指定的服务。

在 [Install] 段使用这些字符串有特定含义: %n, %N, %p, %i, %U, %u, %m, %H, %b. 详细信息看下一段。、

2.4 Install段

Install通常是配置文件的最后一个区块,用来定义如何启动,以及是否开机启动。

  • WantedBy:它的值是一个或多个 target,执行enable命令时,符号链接会放入/etc/systemd/system目录下以 target 名 + .wants后缀构成的子目录中

  • RequiredBy:它的值是一个或多个 target,执行enable命令时,符号链接会放入/etc/systemd/system目录下以 target 名 + .required后缀构成的子目录中

  • Alias:当前 Unit 可用于启动的别名

  • Also:当前 Unit 被 enable/disable 时,会被同时操作的其他 Unit

2.5 Service段

Service 用来 Service 的配置,只有 Service 类型的 Unit 才有这个区块。

2.5.1 启动类型

  • Type:定义启动时的进程行为。它有以下几种值。

    Type=simple:默认值,ExecStart字段启动的进程为主进程
    服务进程不会 fork,如果该服务要启动其他服务,不要使用此类型启动,除非该服务是 socket 激活型

    Type=forking:ExecStart字段将以fork()方式从父进程创建子进程启动,创建后父进程会立即退出,子进程成为主进程。
    通常需要指定PIDFile字段,以便 Systemd 能够跟踪服务的主进程

    对于常规的守护进程(daemon),除非你确定此启动方式无法满足需求,使用此类型启动即可

    Type=oneshot:只执行一次,Systemd 会等当前服务退出,再继续往下执行, 适用于只执行一项任务、随后立即退出的服务
    通常需要指定RemainAfterExit=yes字段,使得 Systemd 在服务进程退出之后仍然认为服务处于激活状态

    Type=dbus:当前服务通过 D-Bus 信号启动。当指定的 BusName 出现在 DBus 系统总线上时,Systemd认为服务就绪

    Type=notify:当前服务启动完毕会发出通知信号,通知 Systemd,然后 Systemd 再启动其他服务

    Type=idle:Systemd 会等到其他任务都执行完,才会启动该服务。一种使用场合是:让该服务的输出,不与其他服务的输出相混合

    2.5.2 启动行为

  • ExecStart:启动当前服务的命令

    1
    2
    3
     
    ExecStart=/bin/echo execstart1
    ExecStart=
    ExecStart=/bin/echo execstart2
     

    顺序执行设定的命令,把字段置空,表示清除之前的值

  • ExecStartPre:启动当前服务之前执行的命令
  • ExecStartPost:启动当前服务之后执行的命令
  • ExecReload:重启当前服务时执行的命令
  • ExecStop:停止当前服务时执行的命令
  • ExecStopPost:停止当前服务之后执行的命令
  • RemainAfterExit:当前服务的所有进程都退出的时候,Systemd 仍认为该服务是激活状态, 这个配置主要是提供给一些并非常驻内存,而是启动注册后立即退出,然后等待消息按需启动的特殊类型服务使用的
  • TimeoutSec:定义 Systemd 停止当前服务之前等待的秒数

2.5.3 重启行为

  • RestartSec:Systemd 重启当前服务间隔的秒数
  • KillMode:定义 Systemd 如何停止服务,可能的值包括:
    control-group(默认值):当前控制组里面的所有子进程,都会被杀掉
    process:只杀主进程(sshd 服务,推荐值)
    mixed:主进程将收到 SIGTERM 信号,子进程收到 SIGKILL 信号
    none:没有进程会被杀掉,只是执行服务的 stop 命令。
  • Restart:定义何种情况 Systemd 会自动重启当前服务,可能的值包括:
    no(默认值):退出后不会重启
    on-success:只有正常退出时(退出状态码为0),才会重启
    on-failure:非正常退出时(退出状态码非0),包括被信号终止和超时,才会重启(守护进程,推荐值)
    on-abnormal:只有被信号终止和超时,才会重启(对于允许发生错误退出的服务,推荐值)
    on-abort:只有在收到没有捕捉到的信号终止时,才会重启
    on-watchdog:超时退出,才会重启
    always:不管是什么退出原因,总是重启

2.5.4 上下文

  • PIDFile:指向当前服务 PID file 的绝对路径。

  • User:指定运行服务的用户

  • Group:指定运行服务的用户组

  • EnvironmentFile:指定当前服务的环境参数文件。该文件内部的key=value键值对,可以用$key的形式,在当前配置文件中获取

    启动sshd,执行的命令是/usr/sbin/sshd -D $OPTIONS,其中的变量$OPTIONS就来自EnvironmentFile字段指定的环境参数文件。

2.6 占位符

在 Unit 文件中,有时会需要使用到一些与运行环境有关的信息,例如节点 ID、运行服务的用户等。这些信息可以使用占位符来表示,然后在实际运行中动态地替换为实际的值。

  • %n:完整的 Unit 文件名字,包括 .service 后缀名
  • %p:Unit 模板文件名中 @ 符号之前的部分,不包括 @ 符号
  • %i:Unit 模板文件名中 @ 符号之后的部分,不包括 @ 符号和 .service 后缀名
  • %t:存放系统运行文件的目录,通常是 “run”
  • %u:运行服务的用户,如果 Unit 文件中没有指定,则默认为 root
  • %U:运行服务的用户 ID
  • %h:运行服务的用户 Home 目录,即 %{HOME} 环境变量的值
  • %s:运行服务的用户默认 Shell 类型,即 %{SHELL} 环境变量的值
  • %m:实际运行节点的 Machine ID,对于运行位置每个的服务比较有用
  • %b:Boot ID,这是一个随机数,每个节点各不相同,并且每次节点重启时都会改变
  • %H:实际运行节点的主机名
  • %v:内核版本,即 “uname -r” 命令输出的内容
  • %%:在 Unit 模板文件中表示一个普通的百分号

特殊字符串

许多设置支持使用特殊的字符串,可以在运行或加载时替换成特定的内容。下表是支持的字符串。

字符串 简介 详细信息
%n 完整的服务名称
%N 不转义的完整服务名称
%p 前缀名 对于实例化的服务,这是前@前面的部分,对于其它的服务,是指去掉后缀(即类型)的部分。
%P 不转义的前缀名
%i 实例名称 对于实例化的服务,这是指 @和后缀之间的部分。
%I 不转义的实例名。
%f 不转义的文件名。 这可以不转义的实例名(如果可用)或前缀名,带有/前缀。
%c 服务的控制组路径。?
%r systemd 的根控制组路径。?
%R systemd 的根控制组路径的父目录。
%t 运行时 Socket 目录。 这可以是 /run (系统管理器) 或 $XDG_RUNTIME_DIR (用户管理器).
%u 用户名 这是服务配置的用户或systemd运行实例的用户(如果没有配置的话)。
%U 用户 UID 这是服务配置的用户UID或systemd运行实例的用户UID(如果没有配置的话)
%h 用户家目录 这是服务配置的用户家目录或systemd运行实例的用户家目录(如果没有配置的话)
%s 用户Shell 这是服务配置的用户shell或systemd运行实例的用户shell(如果没有配置的话)
%m 机器 ID 运行系统的机器 ID ,格式是一个字符串。
%b 启动 ID 运行系统的启动 ID ,格式是一个字符串。.
%H 主机名 运行系统的主机名。
%% 转义 % 一个单百分号.

2.7 模板

在现实中,往往有一些应用需要被复制多份运行,就会用到模板文件

模板文件的写法与普通单元文件基本相同,只是模板文件名是以 @ 符号结尾。例如:[email protected]

通过模板文件启动服务实例时,需要在其文件名的 @ 字符后面附加一个用于区分服务实例的参数字符串,通常这个参数是用于监控的端口号或控制台 TTY 编译号

1
 
systemctl start [email protected]
 

Systemd 在运行服务时,首先寻找跟单元名完全匹配的单元文件,如果没有找到,才会尝试选择匹配模板

例如上面的命令,System 首先会在约定的目录下寻找名为 [email protected] 的单元文件,如果没有找到,而文件名中包含 @ 字符,它就会尝试去掉后缀参数匹配模板文件。对于 [email protected],Systemd 会找到 [email protected] 模板文件,并通过这个模板文件将服务实例化。

3. Target

Target 就是一个 Unit 组,包含许多相关的 Unit 。启动某个 Target 的时候,Systemd 就会启动里面所有的 Unit。从这个意义上说,Target 这个概念类似于”状态点”,启动某个 Target 就好比启动到某种状态。

在传统的 SysV-init 启动模式里面,有 RunLevel 的概念,跟 Target 的作用很类似。不同的是,RunLevel 是互斥的,不可能多个 RunLevel 同时启动,但是多个 Target 可以同时启动。

3.1 target vs sysv-init

  • 默认的 RunLevel(在 /etc/inittab 文件设置)现在被默认的 Target 取代,位置是 /etc/systemd/system/default.target,通常符号链接到graphical.target(图形界面)或者multi-user.target(多用户命令行)。
  • 启动脚本的位置,以前是 /etc/init.d 目录,符号链接到不同的 RunLevel 目录 (比如 /etc/rc3.d、/etc/rc5.d 等),现在则存放在 /lib/systemd/system 和 /etc/systemd/system 目录。
  • 配置文件的位置,以前 init 进程的配置文件是 /etc/inittab,各种服务的配置文件存放在 /etc/sysconfig 目录。现在的配置文件主要存放在 /lib/systemd 目录,在 /etc/systemd 目录里面的修改可以覆盖原始设置。

runlevel是 SysV init 初始化系统中的概念,在Systemd初始化系统中使用的是 Target,他们之间的映射关系是

Runlevel Target 说明
0 poweroff.target 关闭系统
1 rescue.target 维护模式
2,3,4 multi-user.target 多用户,无图形系统(命令行界面)
5 graphical.target 多用户,图形化系统(图形用户界面)
6 reboot.target 重启系统

3.2 target vs unit

如果一个target只包含一个Unit,那么该 target,没有对应的目录,指的就是这个 Unit, 例如 hibernate.target只包含 systemd-hibernate.service一个Unit.

如果一个target包含多个Unit,那么该target,有对应的 xxx.target.wants 目录,指的是目录里面所有的Unit, 例如multi-user.target 包含位于/etc/systemd/system/multi-user.target.wants目录下的多个 Unit.

3.3 target 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 
# 查看当前系统的所有 Target
$ systemctl list-unit-files --type=target

# 查看一个 Target 包含的所有 Unit
$ systemctl list-dependencies multi-user.target

# 查看启动时的默认 Target
$ systemctl get-default

# 设置启动时的默认 Target
$ sudo systemctl set-default multi-user.target

# 切换 Target 时,默认不关闭前一个 Target 启动的进程,systemctl isolate 命令改变这种行为,关闭前一个 Target 里面所有不属于后一个 Target 的进程
$ sudo systemctl isolate multi-user.target
 

3.4 启动过程

  1. 读入 /boot 目录下的内核文件
  2. 内核文件加载完之后,开始执行第一个程序/sbin/init 初始化进程,由 Systemd 初始化系统引导,完成相关的初始化工作
  3. Systemd 执行default.target ,获知设定的启动 target (查看默认 target: systemctl get-default)
  4. Systemd 执行启动 target 对应的单元文件。根据单元文件中定义的[依赖关系](bootup Manager Bootup),传递控制权,依次执行其他 target 单元文件,同时启动每个 target 包含的单元

4. 命令

4.1 系统管理命令

systemctl是 Systemd 的主命令,用于管理系统。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 
# 重启系统
$ sudo systemctl reboot

# 关闭系统,切断电源
$ sudo systemctl poweroff

# CPU停止工作
$ sudo systemctl halt

# 暂停系统
$ sudo systemctl suspend

# 让系统进入冬眠状态
$ sudo systemctl hibernate

# 让系统进入交互式休眠状态
$ sudo systemctl hybrid-sleep

# 启动进入救援状态(单用户状态)
$ sudo systemctl rescue
 

systemd-analyze命令用于查看启动耗时。

1
2
3
4
5
6
7
8
9
10
11
 
# 查看启动耗时
$ systemd-analyze                                                                                       

# 查看每个服务的启动耗时
$ systemd-analyze blame

# 显示瀑布状的启动过程流
$ systemd-analyze critical-chain

# 显示指定服务的启动流
$ systemd-analyze critical-chain atd.service
 

4.2 查看配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
 
# 列出所有配置文件
# 这个列表显示每个配置文件的状态,一共有四种。
# enabled:已建立启动链接
# disabled:没建立启动链接
# static:该配置文件没有[Install]部分(无法执行),只能作为其他配置文件的依赖
# masked:该配置文件被禁止建立启动链接
$ systemctl list-unit-files

# 列出指定类型的配置文件
$ systemctl list-unit-files --type=service

# 查看当前系统的所有 Target
$ systemctl list-unit-files --type=target
 

4.3 查看系统Unit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 
# 列出正在运行的 Unit
$ systemctl list-units

# 列出所有Unit,包括没有找到配置文件的或者启动失败的
$ systemctl list-units --all

# 列出所有没有运行的 Unit
$ systemctl list-units --all --state=inactive

# 列出所有加载失败的 Unit
$ systemctl list-units --failed

# 列出所有正在运行的、类型为 service 的 Unit
$ systemctl list-units --type=service

# 查看 Unit 配置文件的内容
$ systemctl cat docker.service
 

4.4 查看 Unit 的状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 
# 显示系统状态
$ systemctl status

# 显示单个 Unit 的状态
$ systemctl status bluetooth.service

# 显示远程主机的某个 Unit 的状态
$ systemctl -H [email protected] status httpd.service

# 显示某个 Unit 是否正在运行
$ systemctl is-active application.service

# 显示某个 Unit 是否处于启动失败状态
$ systemctl is-failed application.service

# 显示某个 Unit 服务是否建立了启动链接
$ systemctl is-enabled application.service
 

4.5 Unit 的管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 
# 立即启动一个服务
$ sudo systemctl start apache.service

# 立即停止一个服务
$ sudo systemctl stop apache.service

# 重启一个服务
$ sudo systemctl restart apache.service

# 杀死一个服务的所有子进程
$ sudo systemctl kill apache.service

# 重新加载一个服务的配置文件
$ sudo systemctl reload apache.service

# 重载所有修改过的配置文件
$ sudo systemctl daemon-reload

# 显示某个 Unit 的所有底层参数
$ systemctl show httpd.service

# 显示某个 Unit 的指定属性的值
$ systemctl show -p CPUShares httpd.service

# 设置某个 Unit 的指定属性
$ sudo systemctl set-property httpd.service CPUShares=500
 

4.6 查看 Unit 的依赖关系

1
2
3
4
5
 
# 列出一个 Unit 的所有依赖,默认不会列出 target 类型
$ systemctl list-dependencies nginx.service

# 列出一个 Unit 的所有依赖,包括 target 类型
$ systemctl list-dependencies --all nginx.service
 

4.7 服务的生命周期

当一个新的 Unit 文件被放入 /etc/systemd/system/ 或 /usr/lib/systemd/system/ 目录中时,它是不会被自识识别的。

服务的激活

  • systemctl enable:在 /etc/systemd/system/ 建立服务的符号链接,指向 /usr/lib/systemd/system/ 中
  • systemctl start:依次启动定义在 Unit 文件中的 ExecStartPre、ExecStart 和 ExecStartPost 命令

服务的启动和停止

  • systemctl start:依次启动定义在 Unit 文件中的 ExecStartPre、ExecStart 和 ExecStartPost 命令
  • systemctl stop:依次停止定义在 Unit 文件中的 ExecStopPre、ExecStop 和 ExecStopPost 命令
  • systemctl restart:重启服务
  • systemctl kill:立即杀死服务

服务的开机启动和取消

  • systemctl enable:除了激活服务以外,也可以置服务为开机启动
  • systemctl disable:取消服务的开机启动

服务的修改和移除

  • systemctl daemon-reload:Systemd 会将 Unit 文件的内容写到缓存中,因此当 Unit 文件被更新时,需要告诉 Systemd 重新读取所有的 Unit 文件

  • systemctl reset-failed:移除标记为丢失的 Unit 文件。在删除 Unit 文件后,由于缓存的关系,即使通过 daemon-reload 更新了缓存,在 list-units 中依然会显示标记为 not-found 的 Unit。

4.8 systemctl 与 service 命令的区别

  1. systemctl 融合了 service 和 chkconfig 的功能
  2. 在 Ubuntu18.04 中没有自带 chkconfig 命令;service 命令实际上重定向到 systemctl 命令
动作 SysV Init 指令 Systemd 指令
启动某服务 service httpd start systemctl start httpd
停止某服务 service httpd stop systemctl stop httpd
重启某服务 service httpd restart systemctl restart httpd
检查服务状态 service httpd status systemctl status httpd
删除某服务 chkconfig –del httpd 停掉应用,删除其配置文件
使服务开机自启动 chkconfig –level 5 httpd on systemctl enable httpd
使服务开机不自启动 chkconfig –level 5 httpd off systemctl disable httpd
查询服务是否开机自启 chkconfig –list | grep httpd systemctl is-enabled httpd
加入自定义服务 chkconfig –add test systemctl load test
显示所有已启动的服务 chkconfig –list systemctl list-unit-files | grep enabled

5. system 工具集

  • systemctl:用于检查和控制各种系统服务和资源的状态

  • bootctl:用于查看和管理系统启动分区

  • hostnamectl:用于查看和修改系统的主机名和主机信息

    1
    2
    3
    4
    5
     
    # 显示当前主机的信息
    $ hostnamectl
    
    # 设置主机名。
    $ sudo hostnamectl set-hostname levonfly
     
  • journalctl:用于查看系统日志和各类应用服务日志

  • localectl:用于查看和管理系统的地区信息

    1
    2
    3
    4
    5
    6
     
    # 查看本地化设置
    $ localectl
    
    # 设置本地化参数。
    $ sudo localectl set-locale LANG=en_GB.utf8
    $ sudo localectl set-keymap en_GB
     
  • loginctl:用于管理系统已登录用户和 Session 的信息

    1
    2
    3
    4
    5
    6
    7
    8
     
    # 列出当前session
    $ loginctl list-sessions
    
    # 列出当前登录用户
    $ loginctl list-users
    
    # 列出显示指定用户的信息
    $ loginctl show-user ruanyf
     
  • machinectl:用于操作 Systemd 容器

  • timedatectl:用于查看和管理系统的时间和时区信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    # 查看当前时区设置
    $ timedatectl
    
    # 显示所有可用的时区
    $ timedatectl list-timezones                                                                                   
    
    # 设置当前时区
    $ sudo timedatectl set-timezone America/New_York
    $ sudo timedatectl set-time YYYY-MM-DD
    $ sudo timedatectl set-time HH:MM:SS
     
  • systemd-analyze 显示此次系统启动时运行每个服务所消耗的时间,可以用于分析系统启动过程中的性能瓶颈

  • systemd-ask-password:辅助性工具,用星号屏蔽用户的任意输入,然后返回实际输入的内容

  • systemd-cat:用于将其他命令的输出重定向到系统日志

  • systemd-cgls:递归地显示指定 CGroup 的继承链

  • systemd-cgtop:显示系统当前最耗资源的 CGroup 单元

  • systemd-escape:辅助性工具,用于去除指定字符串中不能作为 Unit 文件名的字符

  • systemd-hwdb:Systemd 的内部工具,用于更新硬件数据库

  • systemd-delta:对比当前系统配置与默认系统配置的差异

  • systemd-detect-virt:显示主机的虚拟化类型

  • systemd-inhibit:用于强制延迟或禁止系统的关闭、睡眠和待机事件

  • systemd-machine-id-setup:Systemd 的内部工具,用于给 Systemd 容器生成 ID

  • systemd-notify:Systemd 的内部工具,用于通知服务的状态变化

  • systemd-nspawn:用于创建 Systemd 容器

  • systemd-path:Systemd 的内部工具,用于显示系统上下文中的各种路径配置

  • systemd-run:用于将任意指定的命令包装成一个临时的后台服务运行

  • systemd-stdio- bridge:Systemd 的内部 工具,用于将程序的标准输入输出重定向到系统总线

  • systemd-tmpfiles:Systemd 的内部工具,用于创建和管理临时文件目录

  • systemd-tty-ask-password-agent:用于响应后台服务进程发出的输入密码请求

6. 参考资料

  • Linux Systemd 详细介绍: Unit、Unit File、Systemctl、Target - usmile - 博客园
  • 可能是史上最全面易懂的 Systemd 服务管理教程!( 强烈建议收藏 ) - 云+社区 - 腾讯云
  • Systemd 入门教程:命令篇 - 阮一峰的网络日志
  • Systemd 入门教程:实战篇 - 阮一峰的网络日志
  • systemd 中文手册 [金步国]

systemd服务内容详解 - 周人假的 - 博客园

附录 systemd.exec 中文手册

9http://www.jinbuguo.com/systemd/systemd.service.html

描述

以 ".service" 为后缀的单元文件, 封装了一个被 systemd 监视与控制的进程。

本手册列出了所有专用于此类单元的 配置选项(亦称"配置指令"或"单元属性")。 systemd.unit(5) 中描述了通用于所有单元类型的配置选项, 它们位于 "[Unit]" 与 "[Install]" 小节。 此类单元专用的配置选项 位于 "[Service]" 小节。

其他可用的选项参见 systemd.exec(5) 手册(定义了命令的执行环境), 以及 systemd.kill(5) 手册(定义了如何结束进程), 以及 systemd.resource-control(5) 手册(定义了进程的 资源控制)。

如果要求启动或停止的某个单元文件 不存在, systemd 将会寻找同名的SysV初始化脚本(去掉 .service 后缀), 并根据那个同名脚本, 动态的创建一个 service 单元。 这主要用于与传统的SysV兼容(不能保证100%兼容)。 更多与SysV的兼容性可参见 Incompatibilities with SysV 文档。

服务模板

systemd 服务可以通过 "service@argument.service" 语法接受一个单独的参数。这样的服务被称为"实例化"服务,而不带 argument 参数的服务单元定义被称为"模板"。例如 [email protected] 服务模板接受一个网络接口参数之后, 就生成了一个实例化服务。在服务单元模板文件内部, 可以通过 %-specifiers 引用参数(也称为"实例名")。详见 systemd.unit(5) 手册。

自动依赖

隐含依赖

下列依赖关系是自动隐含的:

  • 设置了 Type=dbus 的服务会自动添加 Requires=dbus.socket 与 After=dbus.socket 依赖。

  • 基于套接字启动的服务会自动添加对关联的 .socket 单元的 After= 依赖。 服务单元还会为所有在 Sockets= 中列出的 .socket 单元自动添加 Wants= 与 After= 依赖。

还有一些 其他依赖关系是由 systemd.exec(5) 与 systemd.resource-control(5) 中的某些资源限制选项自动隐含添加的。

默认依赖

除非明确设置了 DefaultDependencies=no ,否则 service 单元将会自动添加下列依赖关系:

  • Requires=sysinit.targetAfter=sysinit.targetAfter=basic.targetConflicts=shutdown.targetBefore=shutdown.target 。 这样可以确保普通的服务单元: (1)在基础系统启动完毕之后才开始启动,(2)在关闭系统之前先被干净的停止。 只有那些需要在系统启动的早期就必须启动的服务, 以及那些必须在关机动作的结尾才能停止的服务才需要设置 DefaultDependencies=no 。

  • 从同一个模版实例化出来的所有服务单元(单元名称中带有 "@" 字符), 默认全部属于与模版同名的同一个 slice 单元(参见 systemd.slice(5))。 该同名 slice 一般在系统关机时,与所有模版实例一起停止。 如果你不希望像上面这样,那么可以在模版单元中明确设置 DefaultDependencies=no , 并且:要么在该模版文件中明确定义特定的 slice 单元(同样也要明确设置 DefaultDependencies=no)、 要么在该模版文件中明确设置 Slice=system.slice (或其他合适的 slice)。 参见 systemd.resource-control(5) 手册。

选项

每个服务单元文件都必须包含一个 [Service] 小节。 由于此小节中的许多选项也 同时适用于其他类型的单元, 所以本手册仅记录了专用于服务单元的选项。 其他共享的选项参见 systemd.exec(5), systemd.kill(5), systemd.resource-control(5) 手册。 这里只列出仅能用于 [Service] 小节的 选项(亦称"指令"或"属性"):

Type=

设置进程的启动类型。必须设为 simpleexecforkingoneshotdbusnotifyidle 之一:

  • 如果设为 simple (当设置了 ExecStart= 、 但是没有设置 Type= 与 BusName= 时,这是默认值), 那么 ExecStart= 进程就是该服务的主进程, 并且 systemd 会认为在创建了该服务的主服务进程之后,该服务就已经启动完成。 如果此进程需要为系统中的其他进程提供服务, 那么必须在该服务启动之前先建立好通信渠道(例如套接字), 这样,在创建主服务进程之后、执行主服务进程之前,即可启动后继单元, 从而加快了后继单元的启动速度。 这就意味着对于 simple 类型的服务来说, 即使不能成功调用主服务进程(例如 User= 不存在、或者二进制可执行文件不存在), systemctl start 也仍然会执行成功。

  • exec 与 simple 类似,不同之处在于, 只有在该服务的主服务进程执行完成之后,systemd 才会认为该服务启动完成。 其他后继单元必须一直阻塞到这个时间点之后才能继续启动。换句话说, simple 表示当 fork() 函数返回时,即算是启动完成,而 exec 则表示仅在 fork() 与 execve() 函数都执行成功时,才算是启动完成。 这就意味着对于 exec 类型的服务来说, 如果不能成功调用主服务进程(例如 User= 不存在、或者二进制可执行文件不存在), 那么 systemctl start 将会执行失败。

  • 如果设为 forking ,那么表示 ExecStart= 进程将会在启动过程中使用 fork() 系统调用。 也就是当所有通信渠道都已建好、启动亦已成功之后,父进程将会退出,而子进程将作为主服务进程继续运行。 这是传统UNIX守护进程的经典做法。 在这种情况下,systemd 会认为在父进程退出之后,该服务就已经启动完成。 如果使用了此种类型,那么建议同时设置 PIDFile= 选项,以帮助 systemd 准确可靠的定位该服务的主进程。 systemd 将会在父进程退出之后 立即开始启动后继单元。

  • oneshot 与 simple 类似,不同之处在于, 只有在该服务的主服务进程退出之后,systemd 才会认为该服务启动完成,才会开始启动后继单元。 此种类型的服务通常需要设置 RemainAfterExit= 选项。 当 Type= 与 ExecStart= 都没有设置时, Type=oneshot 就是默认值。

  • dbus 与 simple 类似,不同之处在于, 该服务只有获得了 BusName= 指定的 D-Bus 名称之后,systemd 才会认为该服务启动完成,才会开始启动后继单元。 设为此类型相当于隐含的依赖于 dbus.socket 单元。 当设置了 BusName= 时, 此类型就是默认值。

  • notify 与 exec 类似,不同之处在于, 该服务将会在启动完成之后通过 sd_notify(3) 之类的接口发送一个通知消息。systemd 将会在启动后继单元之前, 首先确保该进程已经成功的发送了这个消息。如果设为此类型,那么下文的 NotifyAccess= 将只能设为非 none 值。如果未设置 NotifyAccess= 选项、或者已经被明确设为 none ,那么将会被自动强制修改为 main 。注意,目前 Type=notify 尚不能与 PrivateNetwork=yes 一起使用。

  • idle 与 simple 类似,不同之处在于, 服务进程将会被延迟到所有活动任务都完成之后再执行。 这样可以避免控制台上的状态信息与shell脚本的输出混杂在一起。 注意:(1)仅可用于改善控制台输出,切勿将其用于不同单元之间的排序工具; (2)延迟最多不超过5秒, 超时后将无条件的启动服务进程。

建议对长时间持续运行的服务尽可能使用 Type=simple (这是最简单和速度最快的选择)。 注意,因为 simple 类型的服务 无法报告启动失败、也无法在服务完成初始化后对其他单元进行排序, 所以,当客户端需要通过仅由该服务本身创建的IPC通道(而非由 systemd 创建的套接字或 D-bus 之类)连接到该服务的时候, simple 类型并不是最佳选择。在这种情况下, notify 或 dbus(该服务必须提供 D-Bus 接口) 才是最佳选择, 因为这两种类型都允许服务进程精确的安排 何时算是服务启动成功、何时可以继续启动后继单元。 notify 类型需要服务进程明确使用 sd_notify() 函数或类似的API, 否则,可以使用 forking 作为替代(它支持传统的UNIX服务启动协议)。 最后,如果能够确保服务进程调用成功、服务进程自身不做或只做很少的初始化工作(且不大可能初始化失败), 那么 exec 将是最佳选择。 注意,因为使用任何 simple 之外的类型都需要等待服务完成初始化,所以可能会减慢系统启动速度。 因此,应该尽可能避免使用 simple 之外的类型(除非必须)。另外,也不建议对长时间持续运行的服务使用 idle 或 oneshot 类型。

RemainAfterExit=

当该服务的所有进程全部退出之后, 是否依然将此服务视为活动(active)状态。 默认值为 no

GuessMainPID=

在无法明确定位 该服务主进程的情况下, systemd 是否应该猜测主进程的PID(可能不正确)。 该选项仅在设置了 Type=forking 但未设置 PIDFile= 的情况下有意义。 如果PID猜测错误, 那么该服务的失败检测与自动重启功能将失效。 默认值为 yes

PIDFile=

该服务PID文件的路径(一般位于 /run/ 目录下)。 强烈建议在 Type=forking 的情况下明确设置此选项。 如果设为相对路径,那么表示相对于 /run/ 目录。 systemd 将会在此服务启动完成之后,从此文件中读取主服务进程的PID 。 systemd 不会写入此文件,但会在此服务停止后删除它(若仍然存在)。 PID文件的拥有者不必是特权用户, 但是如果拥有者是非特权用户,那么必须施加如下安全限制: (1)不能是一个指向其他拥有者文件的软连接(无论直接还是间接); (2)其中的PID必须指向一个属于该服务的进程。

BusName=

设置与此服务通信 所使用的 D-Bus 名称。 在 Type=dbus 的情况下 必须明确设置此选项。

ExecStart=

在启动该服务时需要执行的 命令行(命令+参数)。 有关命令行的更多细节, 可参见后文的"命令行"小节。

除非 Type=oneshot ,否则必须且只能设置一个命令行。 仅在 Type=oneshot 的情况下,才可以设置任意个命令行(包括零个), 多个命令行既可以在同一个 ExecStart= 中设置,也可以通过设置多个 ExecStart= 来达到相同的效果。 如果设为一个空字符串,那么先前设置的所有命令行都将被清空。 如果不设置任何 ExecStart= 指令, 那么必须确保设置了 RemainAfterExit=yes 指令,并且至少设置一个 ExecStop= 指令。 同时缺少 ExecStart= 与 ExecStop= 的服务单元是非法的(也就是必须至少明确设置其中之一)。

命令行必须以一个可执行文件(要么是绝对路径、要么是不含任何斜线的文件名)开始, 并且其后的那些参数将依次作为"argv[1] argv[2] …"传递给被执行的进程。 可选的,可以在绝对路径前面加上各种不同的前缀表示不同的含义:

表 1. 可执行文件前的特殊前缀

前缀 效果
"@" 如果在绝对路径前加上可选的 "@" 前缀,那么其后的那些参数将依次作为"argv[0] argv[1] argv[2] …"传递给被执行的进程(注意,argv[0] 是可执行文件本身)。
"-" 如果在绝对路径前加上可选的 "-" 前缀,那么即使该进程以失败状态(例如非零的返回值或者出现异常)退出,也会被视为成功退出,但同时会留下错误日志。
"+" 如果在绝对路径前加上可选的 "+" 前缀,那么进程将拥有完全的权限(超级用户的特权),并且 User=Group=CapabilityBoundingSet= 选项所设置的权限限制以及 PrivateDevices=PrivateTmp= 等文件系统名字空间的配置将被该命令行启动的进程忽略(但仍然对其他 ExecStart=ExecStop= 有效)。
"!" 与 "+" 类似(进程仍然拥有超级用户的身份),不同之处在于仅忽略 User=Group=SupplementaryGroups= 选项的设置,而例如名字空间之类的其他限制依然有效。注意,当与 DynamicUser= 一起使用时,将会在执行该命令之前先动态分配一对 user/group ,然后将身份凭证的切换操作留给进程自己去执行。
"!!" 与 "!" 极其相似,仅用于让利用 ambient capability 限制进程权限的单元兼容不支持 ambient capability 的系统(也就是不支持 AmbientCapabilities= 选项)。如果在不支持 ambient capability 的系统上使用此前缀,那么 SystemCallFilter= 与 CapabilityBoundingSet= 将被隐含的自动修改为允许进程自己丢弃 capability 与特权用户的身份(即使原来被配置为禁止这么做),并且 AmbientCapabilities= 选项将会被忽略。此前缀在支持 ambient capability 的系统上完全没有任何效果。

"@", "-" 以及 "+"/"!"/"!!" 之一,可以按任意顺序同时混合使用。 注意,对于 "+", "!", "!!" 前缀来说,仅能单独使用三者之一,不可混合使用多个。 注意,这些前缀同样也可以用于 ExecStartPre=ExecStartPost=ExecReload=ExecStop=ExecStopPost= 这些接受命令行的选项。

如果设置了多个命令行, 那么这些命令行将以其在单元文件中出现的顺序依次执行。 如果某个无 "-" 前缀的命令行执行失败, 那么剩余的命令行将不会被继续执行, 同时该单元将变为失败(failed)状态。

当未设置 Type=forking 时, 这里设置的命令行所启动的进程 将被视为该服务的主守护进程。

ExecStartPre=ExecStartPost=

设置在执行 ExecStart= 之前/后执行的命令行。 语法规则与 ExecStart= 完全相同。 如果设置了多个命令行, 那么这些命令行将以其在单元文件中出现的顺序 依次执行。

如果某个无 "-" 前缀的命令行执行失败, 那么剩余的命令行将不会被继续执行, 同时该单元将变为失败(failed)状态。

仅在所有无 "-" 前缀的 ExecStartPre= 命令全部执行成功的前提下, 才会继续执行 ExecStart= 命令。

ExecStartPost= 命令仅在 ExecStart= 中的命令已经全部执行成功之后才会运行, 判断的标准基于 Type= 选项。 具体说来,对于 Type=simple 或 Type=idle 就是主进程已经成功启动; 对于 Type=oneshot 来说就是最后一个 ExecStart= 进程已经成功退出; 对于 Type=forking 来说就是初始进程已经成功退出; 对于 Type=notify 来说就是已经发送了 "READY=1" ; 对于 Type=dbus 来说就是已经取得了 BusName= 中设置的总线名称。

注意,不可将 ExecStartPre= 用于 需要长时间执行的进程。 因为所有由 ExecStartPre= 派生的子进程 都会在启动 ExecStart= 服务进程之前被杀死。

注意,如果在服务启动完成之前,任意一个 ExecStartPre=ExecStart=ExecStartPost= 中无 "-" 前缀的命令执行失败或超时, 那么,ExecStopPost= 将会被继续执行,而 ExecStop= 则会被跳过。

ExecReload=

这是一个可选的指令, 用于设置当该服务 被要求重新载入配置时 所执行的命令行。 语法规则与 ExecStart= 完全相同。

另外,还有一个特殊的环境变量 $MAINPID 可用于表示主进程的PID, 例如可以这样使用:

/bin/kill -HUP $MAINPID 

注意,像上例那样,通过向守护进程发送复位信号, 强制其重新加载配置文件,并不是一个好习惯。 因为这是一个异步操作, 所以不适用于需要按照特定顺序重新加载配置文件的服务。 我们强烈建议将 ExecReload= 设为一个 能够确保重新加载配置文件的操作同步完成的命令行。

ExecStop=

这是一个可选的指令, 用于设置当该服务被要求停止时所执行的命令行。 语法规则与 ExecStart= 完全相同。 执行完此处设置的所有命令行之后,该服务将被视为已经停止, 此时,该服务所有剩余的进程将会根据 KillMode= 的设置被杀死(参见 systemd.kill(5))。 如果未设置此选项,那么当此服务被停止时, 该服务的所有进程都将会根据 KillSignal= 的设置被立即全部杀死。 与 ExecReload= 一样, 也有一个特殊的环境变量 $MAINPID 可用于表示主进程的PID 。

一般来说,不应该仅仅设置一个结束服务的命令而不等待其完成。 因为当此处设置的命令执行完之后, 剩余的进程会被按照 KillMode= 与 KillSignal= 的设置立即杀死, 这可能会导致数据丢失。 因此,这里设置的命令必须是同步操作,而不能是异步操作。

注意,仅在服务确实启动成功的前提下,才会执行 ExecStop= 中设置的命令。 如果服务从未启动或启动失败(例如,任意一个 ExecStart=ExecStartPre=ExecStartPost= 中无 "-" 前缀的命令执行失败或超时), 那么 ExecStop= 将会被跳过。 如果想要无条件的在服务停止后执行特定的动作,那么应该使用 ExecStopPost= 选项。 如果服务启动成功,那么即使主服务进程已经终止(无论是主动退出还是被杀死),也会继续执行停止操作。 因此停止命令必须正确处理这种场景,如果 systemd 发现在调用停止命令时主服务进程已经终止,那么将会撤销 $MAINPID 变量。

重启服务的动作被实现为"先停止、再启动"。所以在重启期间,将会执行 ExecStop= 与 ExecStopPost= 命令。 推荐将此选项用于那些必须在服务干净退出之前执行的命令(例如还需要继续与主服务进程通信)。当此选项设置的命令被执行的时候,应该假定服务正处于完全正常的运行状态,可以正常的与其通信。 如果想要无条件的在服务停止后"清理尸体",那么应该使用 ExecStopPost= 选项。

ExecStopPost=

这是一个可选的指令, 用于设置在该服务停止之后所执行的命令行。 语法规则与 ExecStart= 完全相同。 注意,与 ExecStop= 不同,无论服务是否启动成功, 此选项中设置的命令都会在服务停止后被无条件的执行。

应该将此选项用于设置那些无论服务是否启动成功, 都必须在服务停止后无条件执行的清理操作。 此选项设置的命令必须能够正确处理由于服务启动失败而造成的各种残缺不全以及数据不一致的场景。 由于此选项设置的命令在执行时,整个服务的所有进程都已经全部结束, 所以无法与服务进行任何通信。

注意,此处设置的所有命令在被调用之后都可以读取如下环境变量: $SERVICE_RESULT(服务的最终结果), $EXIT_CODE(服务主进程的退出码), $EXIT_STATUS(服务主进程的退出状态)。 详见 systemd.exec(5) 手册。

RestartSec=

设置在重启服务(Restart=)前暂停多长时间。 默认值是100毫秒(100ms)。 如果未指定时间单位,那么将视为以秒为单位。 例如设为"20"等价于设为"20s"。

TimeoutStartSec=

设置该服务允许的最大启动时长。 如果守护进程未能在限定的时长内发出"启动完毕"的信号,那么该服务将被视为启动失败,并会被关闭。 如果未指定时间单位,那么将视为以秒为单位。 例如设为"20"等价于设为"20s"。 设为 "infinity" 则表示永不超时。 当 Type=oneshot 时, 默认值为 "infinity" (永不超时), 否则默认值等于 DefaultTimeoutStartSec= 的值(参见 systemd-system.conf(5) 手册)。

如果一个 Type=notify 服务发送了 "EXTEND_TIMEOUT_USEC=…" 信号, 那么允许的启动时长将会在 TimeoutStartSec= 基础上继续延长指定的时长。 注意,必须在 TimeoutStartSec= 用完之前发出第一个延时信号。当启动时间超出 TimeoutStartSec= 之后,该服务可以在维持始终不超时的前提下,不断重复发送 "EXTEND_TIMEOUT_USEC=…" 信号, 直到完成启动(发送 "READY=1" 信号)。详见 sd_notify(3) 手册。

TimeoutStopSec=

此选项有两个用途: (1)设置每个 ExecStop= 的超时时长。如果其中之一超时, 那么所有后继的 ExecStop= 都会被取消,并且该服务也会被 SIGTERM 信号强制关闭。 如果该服务没有设置 ExecStop= ,那么该服务将会立即被 SIGTERM 信号强制关闭。 (2)设置该服务自身停止的超时时长。如果超时,那么该服务将会立即被 SIGTERM 信号强制关闭(参见 systemd.kill(5) 手册中的 KillMode= 选项)。 如果未指定时间单位,那么将视为以秒为单位。 例如设为"20"等价于设为"20s"。 设为 "infinity" 则表示永不超时。 默认值等于 DefaultTimeoutStopSec= 的值(参见 systemd-system.conf(5) 手册)。

如果一个 Type=notify 服务发送了 "EXTEND_TIMEOUT_USEC=…" 信号, 那么允许的停止时长将会在 TimeoutStopSec= 基础上继续延长指定的时长。 注意,必须在 TimeoutStopSec= 用完之前发出第一个延时信号。当停止时间超出 TimeoutStopSec= 之后,该服务可以在维持始终不超时的前提下,不断重复发送 "EXTEND_TIMEOUT_USEC=…" 信号,直到完成停止。 详见 sd_notify(3) 手册。

TimeoutSec=

一个同时设置 TimeoutStartSec= 与 TimeoutStopSec= 的快捷方式。

RuntimeMaxSec=

允许服务持续运行的最大时长。 如果服务持续运行超过了此处限制的时长,那么该服务将会被强制终止,同时将该服务变为失败(failed)状态。 注意,此选项对 Type=oneshot 类型的服务无效,因为它们会在启动完成后立即终止。 默认值为 "infinity" (不限时长)。

如果一个 Type=notify 服务发送了 "EXTEND_TIMEOUT_USEC=…" 信号, 那么允许的运行时长将会在 RuntimeMaxSec= 基础上继续延长指定的时长。 注意,必须在 RuntimeMaxSec= 用完之前发出第一个延时信号。当运行时间超出 RuntimeMaxSec= 之后,该服务可以在维持始终不超时的前提下,不断重复发送 "EXTEND_TIMEOUT_USEC=…" 信号, 直到运行结束(发送 "STOPPING=1" 信号或直接退出)。详见 sd_notify(3) 手册。

WatchdogSec=

设置该服务的看门狗(watchdog)的超时时长。 看门狗将在服务成功启动之后被启动。 该服务在运行过程中必须周期性的以 "WATCHDOG=1" ("keep-alive ping")调用 sd_notify(3) 函数。 如果在两次调用之间的时间间隔大于这里设定的值, 那么该服务将被视为失败(failed)状态, 并会被强制使用 WatchdogSignal= 信号(默认为 SIGABRT)关闭。 通过将 Restart= 设为 on-failureon-watchdogon-abnormalalways 之一, 可以实现在失败状态下的自动重启该服务。 这里设置的值将会通过 WATCHDOG_USEC= 环境变量传递给守护进程, 这样就允许那些支持看门狗的服务自动启用"keep-alive ping"。 如果设置了此选项, 那么 NotifyAccess= 将只能设为非 none 值。 如果 NotifyAccess= 未设置,或者已经被明确设为 none , 那么将会被自动强制修改为 main 。 如果未指定时间单位,那么将视为以秒为单位。 例如设为"20"等价于设为"20s"。 默认值"0"表示禁用看门狗功能。 详见 sd_watchdog_enabled(3) 与 sd_event_set_watchdog(3) 手册。

Restart=

当服务进程 正常退出、异常退出、被杀死、超时的时候, 是否重新启动该服务。 所谓"服务进程" 是指 ExecStart=ExecStartPre=ExecStartPost=ExecStop=ExecStopPost=ExecReload= 中设置的进程。 当进程是由于 systemd 的正常操作(例如 systemctl stop|restart)而被停止时, 该服务不会被重新启动。 所谓"超时"可以是看门狗的"keep-alive ping"超时, 也可以是 systemctl start|reload|stop 操作超时。

该选项的值可以取 noon-successon-failureon-abnormalon-watchdogon-abortalways 之一。 no(默认值) 表示不会被重启。 always 表示会被无条件的重启。 on-success 表示仅在服务进程正常退出时重启, 所谓"正常退出"是指:退出码为"0", 或者进程收到 SIGHUPSIGINTSIGTERMSIGPIPE 信号之一, 并且 退出码符合 SuccessExitStatus= 的设置。 on-failure 表示 仅在服务进程异常退出时重启, 所谓"异常退出" 是指: 退出码不为"0", 或者 进程被强制杀死(包括 "core dump"以及收到 SIGHUPSIGINTSIGTERMSIGPIPE 之外的其他信号), 或者进程由于 看门狗超时 或者 systemd 的操作超时 而被杀死。

表 2. Restart= 的设置分别对应于哪些退出原因

退出原因(↓) | Restart= (→) no always on-success on-failure on-abnormal on-abort on-watchdog
正常退出 X X
退出码不为"0" X X
进程被强制杀死 X X X X
systemd 操作超时 X X X
看门狗超时 X X X X

注意如下例外情况(详见下文): (1) RestartPreventExitStatus= 中列出的退出码或信号永远不会导致该服务被重启。 (2) 被 systemctl stop 命令或等价的操作停止的服务永远不会被重启。 (3) RestartForceExitStatus= 中列出的退出码或信号将会 无条件的导致该服务被重启。

注意,服务的重启频率仍然会受到由 StartLimitIntervalSec= 与 StartLimitBurst= 定义的启动频率的制约。详见 systemd.unit(5) 手册。只有在达到启动频率限制之后, 重新启动的服务才会进入失败状态。

对于需要长期持续运行的守护进程, 推荐设为 on-failure 以增强可用性。 对于自身可以自主选择何时退出的服务, 推荐设为 on-abnormal

SuccessExitStatus=

额外定义其他的进程"正常退出"状态。 也就是,在退出码"0"、以及表示"正常退出"的 SIGHUPSIGINTSIGTERMSIGPIPE 信号之外, 再额外添加一组表示"正常退出"的退出码或信号。 可以设为一系列 以空格分隔的数字退出码或者信号名称, 例如:

SuccessExitStatus=1 2 8 SIGKILL 

表示当进程的退出码是 1, 2, 8 或被 SIGKILL 信号终止时, 都可以视为"正常退出"。

如果多次使用此选项, 那么最终的结果将是多个列表的合并。 如果将此选项设为空, 那么先前设置的列表 将被清空。

RestartPreventExitStatus=

可以设为一系列 以空格分隔的数字退出码或信号名称, 当进程的退出码或收到的信号与此处的设置匹配时, 无论 Restart= 选项 是如何设置的, 该服务都将无条件的 禁止重新启动。 例如:

RestartPreventExitStatus=1 6 SIGABRT 

可以确保退出码 1, 6 与 SIGABRT 信号 不会导致该服务被自动重启。 默认值为空, 表示完全遵守 Restart= 的设置。 如果多次使用此选项,那么最终的结果将是多个列表的合并。 如果将此选项设为空,那么先前设置的列表将被清空。

RestartForceExitStatus=

可以设为一系列以空格分隔的数字退出码或信号名称, 当进程的退出码或收到的信号与此处的设置匹配时, 无论 Restart= 是如何设置的,该服务都将无条件的被自动重新启动。 默认值为空,表示完全遵守 Restart= 的设置。 如果多次使用此选项,那么最终的结果将是多个列表的合并。 如果将此选项设为空,那么先前设置的列表将被清空。

RootDirectoryStartOnly=

接受一个布尔值。 设为 yes 表示根目录 RootDirectory= 选项(参见 systemd.exec(5) 手册) 仅对 ExecStart= 中的程序有效, 而对 ExecStartPre=ExecStartPost=ExecReload=ExecStop=ExecStopPost= 中的程序无效。 默认值 no 表示根目录对所有 Exec*= 系列选项中的程序都有效。

NonBlocking=

是否为所有基于套接字启动传递的文件描述符设置非阻塞标记(O_NONBLOCK)。 设为 yes 表示除了通过 FileDescriptorStoreMax= 引入的文件描述符之外, 所有 ≥3 的文件描述符(非 stdin, stdout, stderr 文件描述符)都将被设为非阻塞模式。 该选项仅在与 socket 单元 (systemd.socket(5)) 联用的时候才有意义。 对于那些先前已经通过 FileDescriptorStoreMax= 引入的文件描述符则毫无影响。 默认值为 no

NotifyAccess=

设置通过 sd_notify(3) 访问服务状态通知套接字的模式。 可以设为 none(默认值), mainexecall 之一。 none 表示不更新任何守护进程的状态,忽略所有状态更新消息。 main 表示仅接受主进程的状态更新消息。 exec 表示仅接受主进程以及 Exec*= 进程的状态更新消息。 all 表示接受该服务cgroup内所有进程的状态更新消息。 当设置了 Type=notify 或 WatchdogSec= 的时候(见前文),此选项将只能设为非 none 值。 如果 NotifyAccess= 未设置,或者已经被明确设为 none , 那么将会被自动强制修改为 main 。

注意,服务单元的 sd_notify() 通知能够正常工作的前提, 是必须满足如下两个条件之一: (1)在 PID=1 的进程处理通知消息时,发送该通知的进程依然在运行; (2)发送该通知的进程是 systemd 派生的子进程(也就是匹配 main 或 exec 的进程)。 如果服务单元中的某个辅助进程在发送了 sd_notify() 通知之后就立即退出了, 那么 systemd 将有可能来不及将该通知关联到这个服务单元上。 在这种情况下,即使明确设置了 NotifyAccess=all , 该通知也可能会被忽略掉。

Sockets=

设置一个 socket 单元的名称, 表示该服务在启动时应当从它继承套接字文件描述符。 通常并不需要明确设置此选项, 因为所有与该服务同名(不算后缀)的 socket 单元的套接字文件描述符, 都会被自动的 传递给派生进程。

注意: (1)同一个套接字文件描述符可以被传递给多个不同的进程(服务)。 (2)当套接字上有流量进入时, 被启动的可能是另一个不同于该服务的其他服务。 换句话说就是: 套接字单元中的 Sockets= 所指向的服务单元中的 Sockets= 未必要反向指回去。

如果多次使用此选项, 那么最终的结果将是多个socket单元的合集。 如果将此选项设为空, 那么先前设置的所有socket单元 都将被清空。

FileDescriptorStoreMax=

允许在 systemd 中最多为该服务存储多少个使用 "FDSTORE=1" 消息(sd_pid_notify_with_fds(3)) 的文件描述符。默认值为"0"(不存储)。 通过将服务重启过程中不应该关闭的套接字与文件描述符使用这种方法保存起来, 就可以实现让服务在重启(正常重启或崩溃重启)之后不丢失其状态。 进程的状态可以被序列化为一个文件之后保存在 /run 中, 或者保存在一个 memfd_create(2) 内存文件描述符中(这是更好的选择)。 所有被 systemd 暂存的文件描述符都将在该服务重启之后交还给该服务的主进程。 所有被 systemd 暂存的文件描述符都将在遇到如下两种情况时被自动关闭: (1)收到 POLLHUP 或 POLLERR 信号; (2)该服务被彻底停止,并且没有任何剩余的任务需要处理。 如果使用了此选项,那么前文的 NotifyAccess= 应该被设为允许访问 systemd 提供的通知套接字。若未设置 NotifyAccess= ,那么将被隐含的设为 main

USBFunctionDescriptors=

设为一个包含 USB FunctionFS 描述符的文件路径, 以实现 USB gadget 支持。 仅与配置了 ListenUSBFunction= 的 socket 单元一起使用。该文件的内容将被写入 ep0 文件。

USBFunctionStrings=

设为一个包含 USB FunctionFS 字符串的文件路径。 其行为与上面的 USBFunctionDescriptors= 类似。

参见 systemd.exec(5) 与 systemd.kill(5) 手册,以了解更多其他选项。

命令行

本小节 讲解 ExecStart=ExecStartPre=ExecStartPost=ExecReload=ExecStop=ExecStopPost= 选项的命令行解析规则。

如果要一次设置多个命令,那么可以使用分号(;)将多个命令行连接起来。 注意,仅在设置了 Type=oneshot 的前提下,才可以一次设置多个命令。 分号自身 必须用 "\;" 表示。

每个命令行的内部 都以空格分隔, 第一项是要运行的命令, 随后的各项则是命令的参数。 每一项的边界都可以用单引号或双引号界定, 但引号自身最终将会被剥离。 还可以使用C语言风格的转义序列, 但仅可使用下文表格中的转义序列。 最后,行尾的反斜杠("\") 将被视作续行符(借鉴了bash续行语法)。

命令行的语法刻意借鉴了shell中的转义字符与变量展开语法, 但两者并不完全相同。 特别的, 重定向("<", "<<", ">", ">>")、 管道("|")、 后台运行("&"), 以及其他下文未明确提及的符号都不被支持。

要运行的命令(第一项)可以包含空格,但是不能包含控制字符。

可以在各项命令参数中使用 "%" 系列替换标记(详见 systemd.unit(5)手册)。

支持 "${FOO}" 与 "$FOO" 两种不同的环境变量替换方式。 具体说来就是: "${FOO}" 的内容将原封不动的转化为一个单独的命令行参数, 无论其中是否包含空格与引号,也无论它是否为空。 "$FOO" 的内容将被原封不动的插入命令行中, 但对插入内容的解释却遵守一般的命令行解析规则。 后文的两个例子, 将能清晰的体现两者的差别。

如果要运行的命令(第一项)不是一个绝对路径, 那么将会在编译时设定的可执行文件搜索目录中查找。 因为默认包括 /usr/local/bin//usr/bin//bin//usr/local/sbin//usr/sbin//sbin/ 目录, 所以可以安全的直接使用"标准目录"中的可执行程序名称(没必要再使用绝对路径), 而对于非标准目录中的可执行程序,则必须使用绝对路径。建议始终使用绝对路径以避免歧义。 [提示]可以使用 systemd-path search-binaries-default 显示编译时设定的可执行文件搜索目录。

例(1):

Environment="ONE=one" 'TWO=two two'
ExecStart=echo $ONE $TWO ${TWO} 

这将给 /bin/echo 命令依次传递如下四个参数: "one", "two", "two", "two two"

例(2):

Environment=ONE='one' "TWO='two two' too" THREE=
ExecStart=/bin/echo ${ONE} ${TWO} ${THREE}
ExecStart=/bin/echo $ONE $TWO $THREE 

这将导致 /bin/echo 被执行两次。 第一次被依次传递如下三个参数: "'one'", "'two two' too", "" ; 第二次被依次传递如下三个参数: "one", "two two", "too" 。

此外,如果想要传递美元符号($)自身, 则必须使用 "$$" 。 而那些无法在替换时确定内容的变量将被当做空字符串。 注意,不可以在第一项(也就是命令的绝对路径)中使用变量替换。

注意,这里使用的变量必须已经在 Environment= 或 EnvironmentFile= 中定义。 此外,在 systemd.exec(5) 手册的"环境变量"小节中列出的"静态变量"也可以使用。 例如 $USER 就是一个"静态变量", 而 $TERM 则不是。

注意, 这里的命令行并不直接支持shell命令, 但是可以通过模仿下面这个变通的方法来实现:

ExecStart=sh -c 'dmesg | tac' 

例一

ExecStart=echo one ; echo "two two" 

这将导致 echo 被执行两次。 第一次被传递了单独一个 "one" 参数; 第二次被传递了单独一个 "two two" 参数。 因为一次设置了多个命令,所以仅能用于 Type=oneshot 类型。

例二

ExecStart=echo / >/dev/null & \; \
ls 

这表示向 echo 命令传递五个参数: "/", ">/dev/null", "&", ";", "ls"

表 3. 可以在命令行与环境变量中使用的C语言风格的转义序列

转义序列 实际含义
"\a" 响铃
"\b" 退格
"\f" 换页
"\n" 换行
"\r" 回车
"\t" 制表符
"\v" 纵向制表符
"\\" 反斜线
"\"" 双引号
"\'" 单引号
"\s" 空白
"\xxx" 十六进制数 xx 所对应的字符
"\nnn" 八进制数 nnn 所对应的字符

例子

例 1. 简单服务

下面的单元文件创建了一个运行 /usr/sbin/foo-daemon 守护进程的服务。 未设置 Type= 等价于 Type=simple 默认设置。 systemd 执行守护进程之后, 即认为该单元已经启动成功。

[Unit]
Description=简单的Foo服务

[Service]
ExecStart=/usr/sbin/foo-daemon

[Install]
WantedBy=multi-user.target 

注意,本例中的 /usr/sbin/foo-daemon 必须在启动后持续运行到服务被停止。 如果该进程只是为了派生守护进程,那么应该使用 Type=forking

因为没有设置 ExecStop= 选项, 所以在停止服务时,systemd 将会直接向该服务启动的所有进程发送 SIGTERM 信号。 若超过指定时间依然存在未被杀死的进程,那么将会继续发送 SIGKILL 信号。 详见 systemd.kill(5) 手册。

默认的 Type=simple 并不包含任何通知机制(例如通知"服务启动成功")。 要想使用通知机制,应该将 Type= 设为其他非默认值: Type=notify 可用于能够理解 systemd 通知协议的服务; Type=forking 可用于能将自身切换到后台的服务; Type=dbus 可用于能够在完成初始化之后 获得一个 D-Bus 名称的单元。

例 2. 一次性服务

Type=oneshot 用于那些只需要执行一次性动作而不需要持久运行的单元, 例如文件系统检查或者清理临时文件。 此类单元, 将会在启动后一直等待指定的动作完成, 然后再回到停止状态。 下面是一个执行清理动作的单元:

[Unit]
Description=清理老旧的 Foo 数据

[Service]
Type=oneshot
ExecStart=/usr/sbin/foo-cleanup

[Install]
WantedBy=multi-user.target 

注意,在 /usr/sbin/foo-cleanup 执行结束前, 该服务一直处于"启动中"(activating)状态,而一旦执行结束,该服务又立即变为"停止"(inactive)状态。 也就是说,对于 Type=oneshot 类型的服务,不存在"活动"(active)状态。 这意味着,如果再一次启动该服务,将会再一次执行该服务定义的动作。 注意,在先后顺序上晚于该服务的单元, 将会一直等到该服务变成"停止"(inactive)状态后, 才会开始启动。

Type=oneshot 是唯一可以设置多个 ExecStart= 指令的服务类型。 多个 ExecStart= 指令将按照它们出现的顺序依次执行, 一旦遇到错误,就会立即停止,不再继续执行, 同时该服务也将进入"失败"(failed)状态。

例 3. 可停止的一次性服务

有时候, 单元需要执行一个程序以完成某个设置(启动), 然后又需要再执行另一个程序以撤消先前的设置(停止), 而在设置持续有效的时段中,该单元应该视为处于"活动"(active)状态, 但实际上并无任何程序在持续运行。 网络配置服务就是一个典型的例子。 此外,只能启动一次(不可多次启动)的一次性服务, 也是一个例子。

可以通过设置 RemainAfterExit=yes 来满足这种需求。 在这种情况下,systemd 将会在启动成功后将该单元视为处于"活动"(active)状态(而不是"停止"(inactive)状态)。 RemainAfterExit=yes 虽然可以用于所有 Type= 类型, 但是在实践中主要用于 Type=oneshot 和 Type=simple 类型。 对于 Type=oneshot 类型, systemd 一直等到服务启动成功之后,才会将该服务置于"活动"(active)状态。 所以,依赖于该服务的其他单元必须等待该服务启动成功之后,才能启动。 但是对于 Type=simple 类型, 依赖于该服务的其他单元无需等待, 将会和该服务同时并行启动。 下面的类似展示了一个简单的静态防火墙服务:

[Unit]
Description=简单的静态防火墙

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/simple-firewall-start
ExecStop=/usr/local/sbin/simple-firewall-stop

[Install]
WantedBy=multi-user.target 

因为服务启动成功后一直处于"活动"(active)状态, 所以再次执行 systemctl start 命令不会有任何效果。

例 4. 传统的服务

多数传统的守护进程(服务)在启动时会转入后台运行。 systemd 通过 Type=forking 来支持这种工作方式。 对于这种类型的服务,如果最初启动的进程尚未退出, 那么该单元将依然处于"启动中"(activating)状态。 当最初的进程成功退出, 并且至少有一个进程仍然在运行(并且 RemainAfterExit=no), 该服务才会被视为处于"活动"(active)状态。

对于单进程的传统服务,当最初的进程成功退出后, 将会只剩单独一个进程仍然在持续运行, systemd 将会把这个唯一剩余的进程视为该服务的主进程。 仅在这种情况下,才将可以在 ExecReload=ExecStop= … 之类的选项中使用 $MAINPID 变量。

对于多进程的传统服务,当最初的进程成功退出后,将会剩余多个进程在持续运行, 因此,systemd 无法确定哪一个进程才是该服务的主进程。 在这种情况下,不可以使用 $MAINPID 变量。 然而,如果主进程会创建传统的PID文件, 那么应该将 PIDFile= 设为此PID文件的绝对路径, 以帮助 systemd 从该PID文件中读取主进程的PID,从而帮助确定该服务的主进程。 注意,守护进程必须在完成初始化之前写入PID文件, 否则可能会导致 systemd 读取失败 (读取时文件不存在)。

下面是一个 单进程传统服务的示例:

[Unit]
Description=一个单进程传统服务

[Service]
Type=forking
ExecStart=/usr/sbin/my-simple-daemon -d

[Install]
WantedBy=multi-user.target 

参见 systemd.kill(5) 以了解如何 结束服务进程。

例 5. DBus 服务

对于需要在 D-Bus 系统总线上注册一个名字的服务, 应该使用 Type=dbus 并且设置相应的 BusName= 值。 该服务不可以派生任何子进程。 一旦从 D-Bus 系统总线成功获取所需的名字,该服务即被视为初始化成功。 下面是一个典型的 D-Bus 服务:

[Unit]
Description=一个简单的 DBus 服务

[Service]
Type=dbus
BusName=org.example.simple-dbus-service
ExecStart=/usr/sbin/simple-dbus-service

[Install]
WantedBy=multi-user.target 

对于基于 D-Bus 启动的服务 来说, 不可以包含 "[Install]" 小节, 而是应该在对应的 D-Bus service 文件中设置 SystemdService= 选项,例如 (/usr/share/dbus-1/system-services/org.example.simple-dbus-service.service):

[D-BUS Service]
Name=org.example.simple-dbus-service
Exec=/usr/sbin/simple-dbus-service
User=root
SystemdService=simple-dbus-service.service 

参见 systemd.kill(5) 手册以了解如何 结束服务进程。

例 6. 能够通知初始化已完成的服务

Type=simple 类型的服务 非常容易编写, 但是, 无法向 systemd 及时通知 "启动成功"的消息, 是一个重大缺陷。 Type=notify 可以弥补该缺陷, 它支持将"启动成功"的消息及时通知给 systemd 。 下面是一个典型的例子:

[Unit]
Description=Simple notifying service

[Service]
Type=notify
ExecStart=/usr/sbin/simple-notifying-service

[Install]
WantedBy=multi-user.target 

注意, 该守护进程必须支持 systemd 通知协议, 否则 systemd 将会认为该服务一直处于"启动中"(activating)状态,并在超时后将其杀死。 关于如何支持该通知协议,参见 sd_notify(3) 手册。

参见 systemd.kill(5) 手册以了解如何 结束服务进程。

参见

systemd(1), systemctl(1), systemd-system.conf(5), systemd.unit(5), systemd.exec(5), systemd.resource-control(5), systemd.kill(5), systemd.directives(7)

linux系统服务limitnofile,systemd中设置LimitNOFILE=infinity最终却得到65536

systemd中设置LimitNOFILE=infinity最终却得到65536.md

94e50323e347fcc822f2c53ef7675ae3.png

引入

在CentOS7中进行测试,假如设置LimitNOFILE=infinity,按照字面意思来理解,应该是unlimited,可是发现得到的open files limit值是65536,觉得很奇怪,于是做了如下测试

测试

测试机环境情况如下

[root@ipcpu-s7 ~]# cat /etc/redhat-release

CentOS Linux release 7.4.1708 (Core)

[root@ipcpu-s7 ~]# uname -a

Linux ipcpu-s7 3.10.0-693.11.1.el7.x86_64 #1 SMP Mon Dec 4 23:52:40 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

[root@ipcpu-s7 ~]# rpm -qa |grep systemd-sysv

systemd-sysv-219-42.el7_4.4.x86_64

以docker-ce-18.09.6-3.el7.x86_64这个包为例,docker-ce在/usr/lib/systemd/system/docker.service配置文件中预设了 LimitNOFILE=infinity,于是我们得到了如下结果

[root@ipcpu-s7 ~]# cat /proc/`pidof dockerd`/limits |grep files

Max open files 65536 65536 files

如果不设置,或者设置为LimitNOFILE=unlimited(不识别),则得到了1024

[root@ipcpu-s7 ~]# cat /proc/`pidof dockerd`/limits |grep files

Max open files 1024 4096 files

如果设置为42万,LimitNOFILE=420000,那么结果如下

[root@ipcpu-s7 ~]# cat /proc/`pidof dockerd`/limits |grep files

Max open files 420000 420000 files

总结

在CentOS7中systemd的配置中,设置LimitNOFILE=infinity就等于LimitNOFILE=65536,一定要注意,特别是对于需求10万以上文件打开数的人,一定要自行设定。

此配置和docker无关,使用nginx测试效果也是一样。请知悉。

你可能感兴趣的:(linux,运维,服务器)