systemd-单元
一个单元文件可以描述如下内容之一:.service
系统服务、.mount
挂载点、.sockets
sockets、.device
系统设备、.swap
文件路径、.target
启动目标、.timer
systemd管理的计时器。
不同的单元文件类型里面的配置项不同,通用的配置项有 Unit
和Install
。
单元文件可以使用Boolean作为参数作为变量值。正值可以是1
、yes
、true
、on
,负值可以是0
、no
、false
、off
。
单元文件中时间变量值可以带时间单位,例如:s
、min
、h
、d
、w
、ms
、us
。2min 200ms
等同于120200 ms
。如果时间变量的值没有给单位,则默认单位是s
。
以#
或;
开头的行会被忽略掉。你可以用这种方式写备注。在行尾添加反斜线将链接下面一行的内容。
有一些单元的名称包含一个 @ 标记(例如: name@string.service
),这意味着它是模板单元 name@.service
的一个 实例。 string 被称作实例标识符,在 systemctl 调用模板单元时,会将其当作一个参数传给模板单元,模板单元会使用这个传入的参数代替模板中的 %I
指示符。
在实例化之前,systemd 会先检查 name@string.suffix
文件是否存在(如果存在,就直接使用这个文件,而不是模板实例化)。大多数情况下,包含 @ 标记都意味着这个文件是模板。如果一个模板单元没有实例化就调用,该调用会返回失败,因为模板单元中的 %I
指示符没有被替换。
单元之间存在隐形依赖,依赖关系取决于单元类型和单元配置。这些隐形依赖可以时单位文件更简洁。不同单元文件类型中的隐形依赖关系可能不一样。例如,对于.service
服务类型单元文件,Type=dbus
自动获取获取对dbus.socket
的Requires=
和After=
依赖关系。
在系统模式下单元文件可以从下面两个地方加载,优先级从低到高:
/usr/lib/systemd/system/
:软件包安装的单元/etc/systemd/system/
:系统管理员安装的单元
注意: 用户运行时模式下加载路径不一样。
单元管理命令
下面命令是最长用到的一些命令
立即激活单元:
1 | $ sudo systemctl start <单元> |
立即停止单元:
1 | $ sudo systemctl stop <单元> |
杀死一个服务的所有子进程:
1 | $ sudo systemctl kill <单元> |
重启单元:
1 | $ sudo systemctl restart <单元> |
重新加载配置:
1 | $ sudo systemctl reload <单元> |
输出单元运行状态:
1 | $ sudo systemctl status <单元> |
检查单元是否配置为自动启动:
1 | $ sudo systemctl is-enabled <单元> |
开机激活单元:
1 | $ sudo systemctl enable <单元> |
设置单元为自动启动并立即启动这个单元:
1 | $ sudo systemctl enable --now <单元> |
取消开机自动激活单元:
1 | $ sudo systemctl disable <单元> |
禁用一个单元(禁用后,间接启动也是不可能的):
1 | $ sudo systemctl mask <单元> |
取消禁用一个单元:
1 | $ sudo systemctl unmask <单元> |
显示单元的手册页(必须由单元文件提供):
1 | $ sudo systemctl help <单元> |
重新载入 systemd,扫描新的或有变动的单元:
1 | $ sudo systemctl daemon-reload <单元> |
显示某个Unit的所有底层参数
1 | $ sudo systemctl show <单元> |
显示某个 Unit 的指定属性的值
1 | $ sudo systemctl show -p CPUShares httpd.service |
设置某个 Unit 的指定属性
1 | sudo systemctl set-property httpd.service CPUShares=500 |
提示:
- 上面的大部分命令可以跟多个单元名,详细参见 systemctl(1)
systemctl
命令在enable
、disable
和mask
子命令中增加了--now
选项,可以实现激活的同时启动服务,取消激活的同时停止服务。- 一个软件包可能会提供多个不同的单元。如果你已经安装了软件包,可以通过
pacman -Qql package | grep systemd
命令检查这个软件包提供了哪些单元。
在查看服务状态中:
1 | $ sudo systemctl status httpd |
上面的输出结果含义如下。
- Loaded行:配置文件的位置,是否设为开机启动
- Active行:表示正在运行
- Main PID行:主进程ID
- Status行:由应用本身(这里是 httpd )提供的软件当前状态
- CGroup块:应用的所有子进程
- 日志块:应用的日志
[Unit] 部分
Unit部分包含了单元文件不依赖单元类型的部分。主要是描述单元的启动顺序和依赖关系。
Description=
单元在用户界面下的介绍信息。帮助用户快速了解单元文件用途。
Documetation=
单位文件或配置的URL引用路径,接收以空格分隔的多个URL。URL支持的类型有:http://
、https://
、file:
、info:
、man:
。
Requires=
描述此单元强依赖的单元列表(多个单元文件用空格分隔)。如果此单位被激活,则它依赖的单元也会被激活。
此字段一般结合After=
、Before=
描述依赖顺序的字段一起使用。如果依赖的单元并没有写进顺序启动字段中,那么此单位和依赖的单元会同时被激活没有任何延迟。
如果依赖单元启动失败则此单位也会被退出。为了系统的稳定性,最好的选择是使用Wants=
字段。描述的此单元文件弱依赖的单位文件,如果依赖的单位文件启动失败或异常退出不影响此单位文件。
Wants=
描述此单元的弱依赖单位列表。如果这个列表中的有单位启动失败或异常退出,并不影响此单位正常的运行。
Requisite=
描述单位文件超强依赖单位列表。如果列表中的没有启动,他们则不会立即启动而整个业务失败。
BindsTo=
描述相互绑定的单位列表,如果关联中的单位有停止,这此单位也会停止。
PartOf=
描述关联的单位。如果这个单元被停止或重启了,此单元也会被停止或重启。这个关联是单向的,改变此单元并不影响关联的单元。
Conflicts=
描述相冲突的单元列表。如果冲突列表中单元有启动中的,则会停止此单元。反之,如果此单元启动,停止冲突单元列表中在运行的单元。
Before=, After=
这两个字段设置此单元依赖的其他单元的顺序列表。例如:如果foo.service
单位文件配置了Before=bar.service
。然后这两个单元同时启动,则bar.service
需要等到foo.service
完全启动了再启动。这个两个字段只定义单元之间的启动顺序,不涉及单元依赖。
OnFailure=
描述了当此单元状态进入failed
时需要激活的单元列表。
[Service]部分
Service部分是写在.service
结尾的服务类型单元中的。.service
结尾的单位负责管理和监控systemd进程。
隐形依赖
- 服务定义
Type=dbus
,则自动添加了Requirres=dbus.socket
和After=dbus.socket
- Socket激活的Services会自动在Socket单元激活后再启动。也就是自动将Service添加如
.socket
单元中的After=
。
默认依赖
除非在[Unit]中设置DefaultDependencies=no
,否则就存在下面默认依赖:
- 服务单元默认在
Requires=
和After=
写入sysinit.target
。basic.target
默认写入After=
,shutdown.target
默认写入Conflicts=
和Before=
。这些依赖确保服务单元能够正常进入系统初始化和系统在关闭前被彻底关闭。
Type=
定义服务单元在启动是进程类型。拥有的类型有:simple
、forking
、oneshot
、dbus
,notify
、idle
。
simple
(默认值):ExecStart字段启动的进程为主进程forking
:ExecStart字段将以fork()方式启动,此时父进程将会退出,子进程将成为主进程oneshot
:类似于simple,但只执行一次,Systemd 会等它执行完,才启动其他服务dbus
:类似于simple,但会等待 D-Bus 信号后启动notify
:类似于simple,启动结束后会发出通知信号,然后 Systemd 再启动其他服务idle
:类似于simple,但是要等到其他任务都执行完,才会启动该服务。一种使用场合是为让该服务的输出,不与其他服务的输出相混合
RemainAfterExit=
接收一个Boolean值。说明是否在服务进程都退出的情况下保留服务。默认是no
应用例子:
1 | [Unit] |
上面例子是:笔记本电脑启动时,要把触摸板关掉。启动类型是oneshot
表面这个服务只要运行一次就够了。但关闭后将来某个时间想打开,可以修改配置文件如下:
1 | [Unit] |
RemainAfterExit
字段是yes
,表明在进程退出后服务仍然保持执行。这样在执行systemctl stop
命令停止服务时,ExecStop
指定的命令就会执行,从而重新开启触摸板。
ExecStart=
启动进程时执行的命令。
除非Type
是oneshot
,则必须给个命令。可以定义多个ExecStart
字段,但如果有ExecStart
字段为空值,则这个字段上面定义的ExecStart
字段将无效。
如果没有指定ExecStart=
,则该服务必须具有RemainAfterExit=yes
并且至少有一个ExecStop=
。
每个指定的命令第一个参数必须是可执行的文件绝对路径。或者,这个文件名可能带有许多个特殊符前缀。
特殊的可执行文件前缀。
Prefix | Effect |
---|---|
@ |
可执行文件前缀是@ ,则第二个指定的标记将会作为参数argv[0] 传递给进程 |
- |
可执行文件前缀是- ,则该命令退出代码被认为失败状态(没有退出状态或由于信号不正常退出),会被忽略掉认为是成功退出 |
+ |
可执行文件前缀是+ 则该进程具有完全执行权限。这个权限模式下限制配置的User=, Group=, CapabilityBoundingSet= `或者变量文件系统命名空间操作,在命令行是不会被执行应用的。 |
! |
Similar to the “+” character discussed above this permits invoking command lines with elevated privileges. However, unlike “+” the “!” character exclusively alters the effect of User=, Group= and SupplementaryGroups=, i.e. only the stanzas the affect user and group credentials. Note that this setting may be combined with DynamicUser=, in which case a dynamic user/group pair is allocated before the command is invoked, but credential changing is left to the executed process itself. |
!! |
This prefix is very similar to “!!”, however it only has an effect on systems lacking support for ambient process capabilities, i.e. without support for AmbientCapabilities=. It’s intended to be used for unit files that take benefit of ambient capabilities to run processes with minimal privileges wherever possible while remaining compatible with systems that lack ambient capabilities support. Note that when “!!” is used, and a system lacking ambient capability support is detected any configured SystemCallFilter= and CapabilityBoundingSet= stanzas are implicitly modified, in order to permit spawned processes to drop credentials and capabilities themselves, even if this is configured to not be allowed. Moreover, if this prefix is used and a system lacking ambient capability support is detected AmbientCapabilities= will be skipped and not be applied. On systems supporting ambient capabilities, “!!” has no effect and is redundant. |
ExecStartPre= ExecStartPost=
ExecStartPre
字段:启动服务之前执行的命令ExecStartPost
字段:启动服务之后执行的命令
ExecReload=
触发重新加载时的命令。
ExecStop=
停止经由ExecStart=
启动的服务。
ExecStopPost=
服务停止后执行的命令。
KillMode=
killMode
字段属于systemd.kill
。根据单元类型(service.service
,socket.socket
, mount.mount
, swap.swap
, scope.scope
)可以选择配置。
指定进程kill方式(control-group
、process
、mixed
、none
)。
control-group
:当前控制组里面的所有子进程,都会被杀掉。process
:只杀主进程,不停止任何子进程。mixed
:主进程将收到 SIGTERM 信号,子进程收到 SIGKILL 信号。none
:没有进程会被杀掉,只是执行服务的 stop 命令。
RestartSec=
配置重新启动服务之前的休眠时间
TimeoutStartSec=
配置单元启动最长时间,如果超过这个时间将视为启动失败。
TimeoutStopSec=
配置单元停止最长的时间,但是在指定的时间内没有终止,那么它将被SIGTERM强制终止。
TimeoutSec=
将TimeoutStartSec=
和TimeoutStopSec=
配置为指定值的简写形式。
Restart=
配置服务是否在服务进程退出,终止或达到超时时重新启动。
Restart字段可以设置的值如下。:
no
(默认值):退出后不会重启on-success
:只有正常退出时(退出状态码为0),才会重启on-failure
:非正常退出时(退出状态码非0),包括被信号终止和超时,才会重启on-abnormal
:只有被信号终止和超时,才会重启on-abort
:只有在收到没有捕捉到的信号终止时,才会重启on-watchdog
:超时退出,才会重启always
:不管是什么退出原因,总是重启
RestartSec=
重启服务之前,需要等待的时间。
[Install]部分
Install
区块,定义如何安装这个配置文件,即怎样做到开机启动。
Alias=
创建匿名单位列表。列表中的单元名字后缀需要和单位文件后缀一致。在systemctl enable
中会创建软连接将列表名字和单元文件名联系起来。
并不是所有单元类型都支持Alias
。mount
, slice
, swap
, 和 automount
单元类型不支持。
WantBy= RequiredBy=
这两个字段可以有多个或者使用单元列表。当执行systemclt enable
时会在列出的每个单元的.wants/
或.requires/
目录下创建此单元的一个符号链接。
当列表中的单元启动时,此单元也会被启动。
这个两个字段表示Target,一个Target可以包含多个单元,但Target被启动时,里面所包含的单元也会跟在启动。
一般Target就是运行模式。
查看当前运行模式:
1 | $ systemctl get-default |
查看 multi-user.target 包含的所有服务:
1 | $ systemctl list-dependencies multi-user.target |
切换target
1 | $ sudo systemctl isolate shutdown.target |
修改配置文件后重启
修改配置文件以后,需要重新加载配置文件,然后重新启动相关服务。
1 | # 重新加载配置文件 |
参考
https://www.freedesktop.org/software/systemd/man/systemd.unit.html#WantedBy=
http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html
作者: Fynn
链接: https://fynn90.github.io/2017/11/30/systemd-%E5%8D%95%E5%85%83/
本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可