名称
systemd.service
- 服务单元配置
概要
service.service
描述
以".service
"结尾的单元配置文件编码了由 systemd
控制和监督的进程信息。
本手册列出了该单元类型的特定配置选项。所有单元配置文件的通用选项参见 systemd.unit(5)
。通用配置项在通用的 [Unit]
和 [Install]
部分进行配置。服务专用配置选项在 [Service]
部分配置。
systemd.exec(5)
、systemd.kill(5)
和 systemd.resource-control(5)
中还列出了其他选项,前者定义了命令的执行环境,后者定义了服务进程的终止方式,最后者配置了服务进程的资源控制设置。
如果启用了 SysV init compat
,systemd
会自动创建封装 SysV init
脚本的服务单元(服务名称与脚本名称相同,但添加了".service
"后缀);请参阅 systemd-sysv-generator(8)
。
systemd-run(1)
命令允许从命令行动态和临时创建 .service
和 .scope
单元。
服务模板
通过 "service@argument.service
"语法,systemd
服务可以接受一个参数。此类服务被称为 "实例化 "服务,而不带参数的单元定义则被称为 “模板”。例如,dhcpcd@.service
服务模板将网络接口作为参数,形成一个实例化服务。在服务文件中,该参数或 "实例名 "可通过 %-
指定符访问。详情请参阅 systemd.unit(5)
。
自动依赖关系
隐式依赖
以下依赖项被隐式添加:
- 设置了
Type=dbus
的服务会自动获取dbus.socket
上Requires=
和After=
类型的依赖关系。 - 通过自动
After=
依赖关系,套接字激活的服务会在其激活的.socket
单元之后自动排序。服务也会通过自动Wants=
和After=
依赖关系拉入Sockets=
中列出的所有.socket
单元。
根据 systemd.exec(5)
和 systemd.resource-control(5)
中记录的执行和资源控制参数,可能会添加其他隐式依赖关系。
默认依赖
除非设置DefaultDependencies=no
,否则会添加以下依赖项:
- 服务单元对
sysinit.target
有Requires=
和After= 类型
的依赖关系,对basic.target
有After=
类型的依赖关系,对shutdown.target
有Conflicts=
和Before=
类型的依赖关系。这些都能确保正常的服务单元在基本系统初始化时启动,并在系统关闭前干净利落地终止。只有涉及提前启动或延迟系统关闭的服务才应禁用此选项。 - 实例服务单元(即名称中带有"
@
"的服务单元)默认为每个模板分配一个片段单元(参见systemd.slice(5)
),以模板单元命名,其中包含特定模板的所有实例。通常在关机时,该分片会连同所有模板实例一起停止。如果不希望这样,可在模板单元中设置DefaultDependencies=no
,然后定义自己的每个模板片段单元文件,也设置DefaultDependencies=no
,或者在模板单元中设置Slice=system.slice
(或其他合适的片段)。另请参见systemd.resource-control(5)
。
选项
服务单元文件可能包括 [Unit]
和 [Install]
部分,这在 systemd.unit(5)
中有所描述。
服务单元文件必须包含一个 [Service]
部分,其中包含有关服务及其监控进程的信息。该部分可能使用的一些选项与其他单元类型共享。这些选项在 systemd.exec(5)
、systemd.kill(5)
和 systemd.resource-control(5)
中都有说明。服务单元 [Service]
部分的特定选项如下:
Type=
配置服务通知管理器服务启动已完成的机制。可选择simple
、exec
、forking
、oneshot
、dbus
、notify
、notify-reload
或idle
中的一个:
-
如果设置为
simple
(指定ExecStart=
但未指定Type=
或BusName=
时的默认值),服务管理器将认为该单元是在主服务进程被fork
后立即启动的(即在fork()
之后、各种进程属性被配置之前,尤其是在新进程调用execve()
以调用实际服务二进制文件之前)。通常,Type=exec
是更好的选择,请参见下文。预期配置
ExecStart=
的进程是服务的主进程。在这种模式下,如果该进程向系统中的其他进程提供功能,则应在服务启动前安装其通信通道(例如,由systemd
通过套接字激活设置的套接字),因为服务管理器会在创建主服务进程后,在执行服务的二进制文件前,立即启动后续单元。请注意,这意味着即使无法成功调用服务的二进制文件(例如所选User=
不存在或服务二进制文件丢失),简单服务的systemctl
启动命令行也会报告成功。 -
exec
类型与simple
类型类似,但服务管理器会在主服务二进制执行完毕后立即启动该单元。服务管理器会将后续单元的启动推迟到这一时刻。(或者换句话说:simple
会在fork()
返回后立即继续执行后续任务,而exec
不会在服务进程中的fork()
和execve()
都成功之前继续执行后续任务)。请注意,这意味着当无法成功调用服务的二进制文件时,exec
服务的systemctl start
命令行将报告失败(例如,由于所选User=
不存在,或服务二进制文件丢失)。 -
如果设置为
forking
,则管理器将认为在被管理器fork
的二进制文件退出后立即启动的单元。不鼓励使用这种类型,而是使用notify
、notify-reload
或dbus
。预期配置为
ExecStart=
的进程在启动时会调用fork()
。预期父进程将在启动完成且所有通信通道建立后退出。子进程将继续作为主服务进程运行,当父进程退出时,服务管理器将认为该单元已启动。这是传统 UNIX 服务的行为。如果使用此设置,建议同时使用PIDFile=
选项,以便systemd
能可靠地识别服务的主进程。管理器将在父进程退出后继续启动后续单元。 -
oneshot
的行为类似于simple
;不过,服务管理器会在主进程退出后将该单元视为已启动。然后,它将启动后续单元。RemainAfterExit=
对这类服务特别有用。如果既不指定Type=
也不指定ExecStart=
,则Type=oneshot
为隐含默认值。请注意,如果使用该选项而不使用RemainAfterExit=
,服务将永远不会进入 "激活 "单元状态,而是直接从 "激活 "过渡到 "停用 "或 “死机”,因为没有配置持续运行的进程。这尤其意味着,此类服务运行(且未设置RemainAfterExit=
)后,不会显示为已启动,而是显示为已死亡。 -
dbus
的行为类似于simple
;但是,这种类型的单元必须指定BusName=
,当指定的总线名称被获取后,服务管理器将认为该单元已启动。如果指定了BusName=
,则默认使用此类型。配置了该选项的服务单元会隐式获得对
dbus.socket
单元的依赖性。在获得指定的总线名称之前,这种类型的服务单元被视为处于激活状态。当总线名称被占用时,它被认为处于激活状态。一旦总线名称被释放,服务就会被认为不再起作用,服务管理器会尝试终止属于该服务的任何剩余进程。因此,作为关闭逻辑的一部分而放弃总线名称的服务应做好接收SIGTERM
(或KillSignal=
中配置的信号)的准备。 -
notify
的行为与exec
类似;不过,当服务启动完成后,它应该会通过sd_notify(3)
或类似调用发送一条 "READY=1
"的通知消息。 系统会在发送这条通知消息后继续启动后续单元。如果使用此选项,则应设置NotifyAccess=
(见下文),以打开对systemd
提供的通知套接字的访问。如果NotifyAccess=
未设置或设置为none
,则会强制设置为main
。如果服务支持重载并使用信号启动重载,建议使用
notify-reload
代替。 -
notify-reload
的行为与notify
类似,但有一点不同:当服务被要求重新加载时,SIGHUP
UNIX 进程信号将被发送到服务的主进程,管理器将等待重新加载完成的通知。在启动重载进程时,服务应通过
sd_notify(3)
回复一条通知消息,其中包含 "RELOADING=1
"字段,以及设置为当前单调时间(即clock_gettime(2)
中的CLOCK_MONOTONIC
)的 “MONOTONIC_USEC=
”,单位为 μs,格式为十进制字符串。重载完成后,必须发送另一条包含 "READY=1
"的通知消息。使用此服务类型并执行此重载协议,比提供ExecReload=
命令来重载服务配置更有效。发送的信号可通过
ReloadSignal=
调整,见下文。 -
idle
的行为与simple
非常相似;不过,服务程序的实际执行会延迟到所有活动任务都已派发完毕。这可以用来避免 shell 服务的输出与控制台的状态输出交错。需要注意的是,这种类型只能用于改善控制台输出,不能作为一般的单元排序工具,而且这种服务类型的效果会受到 5 秒超时的限制,超时后服务程序还是会被调用。
建议对长时间运行的服务使用Type=exec
,因为它可以确保正确跟踪进程设置错误(例如缺少服务可执行文件或缺少用户等错误)。然而,由于这种服务类型不会在服务自己的启动代码中传播失败(与服务管理器在execute()
之前执行的准备步骤中的失败相反),并且不允许在服务代码本身初始化完成时对其他单元进行排序(例如,如果客户端需要通过某种形式的IPC
连接到服务,IPC
通道仅由服务本身建立)与通过套接字或总线激活(或类似的方式)提前执行此操作相比,这在许多情况下可能还不够。如果是这样,notify
、notify-reload
或dbus(
后者仅在服务提供D-Bus接口的情况下使用)是首选选项,因为它们允许服务程序代码精确地调度何时考虑服务成功启动以及何时继续执行后续单元。notify/notify-reload
服务类型需要在服务代码库中得到显式支持(因为服务需要在适当的时候调用sd_notify()
或等效的API)——如果不支持,那么fork
是一种替代方案:它支持传统的重量级UNIX服务启动协议。请注意,使用simple
以外的任何类型都可能延迟引导过程,因为服务管理器至少需要等待一些服务初始化完成。(另请注意,通常不建议对长时间运行的服务使用idle
或oneshot
。)
请注意,各种服务设置(如通过 libc NSS
进行的 User=
、Group=
)在使用时可能会导致对其他服务的 "隐藏 "阻塞 IPC
调用。有时,最好使用simple
服务类型,以确保服务管理器的事务逻辑不受此类潜在的缓慢操作和隐藏依赖关系的影响,因为这是唯一一种服务类型,服务管理器不会等待此类服务执行设置操作完成后再继续。
ExitType=
指定管理器认为服务已完成的时间。可选 main
或 cgroup
:
- 如果设置为
main
(默认值),服务管理器将在根据Type=
确定的主进程退出时认为该单元已停止。因此,它不能与Type=oneshot
一起使用。 - 如果设置为
cgroup
,只要cgroup
中至少有一个进程没有退出,服务就会被视为正在运行。
一般建议在服务有已知的forking
模型且能可靠确定主进程时使用 ExitType=main
。ExitType= cgroup
适用于forking
模型无法提前获知且可能没有特定主进程的应用程序。它非常适合瞬时或自动生成的服务,如桌面环境中的图形应用程序。
在250版添加。
RemainAfterExit=
接受一个布尔值,用于指定即使服务的所有进程都已退出,服务是否仍被视为活动状态。默认值为 no
。
GuessMainPID=
接受一个布尔值,用于指定在无法可靠确定服务主 PID 的情况下,systemd
是否应尝试猜测该 PID。除非设置了Type=forking
且未设置 PIDFile=
,否则该选项将被忽略,因为对于其他类型或明确配置的 PID 文件,主 PID 始终是已知的。如果守护进程由多个进程组成,猜测算法可能会得出错误结论。如果无法确定主 PID,服务的故障检测和自动重启将无法可靠运行。默认为yes
。
PIDFile=
接受指向服务 PID 文件的路径。建议将 Type=
设置为forking
的服务使用该选项。指定的路径通常指向 /run/
下面的文件。如果指定的是相对路径,则以 /run/
作为前缀。服务启动后,服务管理器将从该文件读取服务主进程的 PID。服务管理器不会向此处配置的文件写入内容,但如果该文件仍然存在,则会在服务关闭后将其删除。PID 文件不需要由特权用户拥有,但如果由非特权用户拥有,则会执行额外的安全限制:该文件不得是不同用户拥有的文件的符号链接(既不能是直接链接,也不能是间接链接),而且 PID 文件必须指向已属于服务的进程。
注意,在现代项目中应该避免使用PID文件。尽可能使用Type=notify
、Type=notify-reload
或Type=simple
,这样不需要使用PID文件来确定服务的主进程,并避免不必要的forking
。
BusName=
接受该服务应使用的 D-Bus
目的地名称。对于 Type=
设置为 dbus
的服务,该选项是强制性的。建议在已知的情况下始终设置该属性,以便于将服务名称映射到 D-Bus 目标地址。特别是,systemctl service-log-level/service-log-target
等动词都会使用该选项。
ExecStart=
服务启动时执行的命令。根据下面“命令行”一节描述的规则,将该值拆分为零个或多个命令行。
除非 Type=
为 oneshot
,否则必须给出一条命令。使用 Type=oneshot
时,可以指定零条或多条命令。可以通过在同一指令中提供多条命令行来指定命令,也可以多次指定该指令,但效果相同。如果为该选项指定了空字符串,要启动的命令列表将被重置,之前指定的该选项将不起作用。如果未指定 ExecStart=
,则服务必须有 RemainAfterExit=yes
和至少一个 ExecStop=
行。(同时缺少 ExecStart=
和 ExecStop=
的服务无效)。
如果指定了多条命令,则会按照命令在单元文件中出现的顺序依次调用。如果其中一条命令失败(且前缀没有"-
"),其他行将不会被执行,单元将被视为失败。
除非设置了Type=forking
,否则通过该命令行启动的进程将被视为守护进程的主进程。
ExecStartPre=, ExecStartPost=
分别在 ExecStart=
命令之前或之后执行的附加命令。语法与 ExecStart=
相同,但允许使用多条命令行,而且命令会一条接一条地连续执行。
如果其中任何一条命令(不以"-
"为前缀)失败,其余命令将不会执行,该单元将被视为失败。
ExecStart=
命令仅在所有未以"-
"为前缀的 ExecStartPre=
命令成功退出后运行。
ExecStartPost=
命令只有在 ExecStart=
中指定的命令被成功调用后才会运行,具体取决于 Type=
(即:对于 Type=simple
或 Type=idle
,进程已启动;对于 Type=oneshot
,最后一个 ExecStart=
进程已成功退出;对于 Type=forking
,初始进程已成功退出;对于 Type=notify/Type=notify-reload
,已发送 “READY=1
”;或者对于 Type=dbus
,已获取 BusName=
)。
请注意,ExecStartPre=
不可用于启动长期运行的进程。所有由通过 ExecStartPre=
调用的进程派生出来的进程都将在下一个服务进程运行前被杀死。
请注意,如果 ExecStartPre=
、ExecStart=
或 ExecStartPost=
中指定的任何命令失败(且未以"-
"为前缀,见上文)或在服务完全启动前超时,将继续执行 ExecStopPost=
中指定的命令,跳过 ExecStop=
中的命令。
请注意,执行 ExecStartPost=
时会考虑 Before=/After=
排序限制。
ExecCondition=
在 ExecStartPre=
中的命令之前执行的可选命令,语法与 ExecStart=
相同,但允许使用多行命令,而且命令会逐条执行。
其行为类似于 ExecStartPre=
和条件检查混合:当 ExecCondition=
命令以退出代码 1 至 254(含 254)退出时,其余命令将被跳过,单元不会被标记为失败。但是,如果 ExecCondition=
命令的退出代码为 255 或异常退出(如超时、被信号杀死等),单元将被视为失败(跳过其余命令)。退出代码为 0 或符合 SuccessExitStatus=
的命令将继续执行下一条命令。
ExecCondition=
也将运行 ExecStopPost=
中的命令,作为停止服务的一部分。
在243版添加。
ExecReload=
用于触发服务配置重载的命令。此参数可接收多行命令,其方案与上述 ExecStart=
相同。此设置的使用是可选的。此处支持指定符和环境变量的替换,其方案与 ExecStart=
相同。
还将设置一个额外的特殊环境变量:如果已知,$MAINPID
将被设置为守护进程的主进程,并可用于执行类似下面的命令行:
ExecReload=kill -HUP $MAINPID
但要注意的是,通过查询信号重载守护进程(如上面的示例行)通常不是一个好的选择,因为这是一个异步操作,因此不适合在多个服务相互重载时进行排序。因此,强烈建议使用 Type=notify-reload
代替 ExecReload=
,或者将 ExecReload=
设置为不仅能触发守护进程的配置重载,而且能同步等待重载完成的命令。例如,dbus-broker(1)
使用以下命令:
ExecReload=busctl call org.freedesktop.DBus \
/org/freedesktop/DBus org.freedesktop.DBus \
ReloadConfig
ExecStop=
要执行的命令,以停止通过 ExecStart=
启动的服务。 此参数可包含多行命令,与上述 ExecStart=
相同。此设置为可选项。运行此选项中配置的命令后,服务即被停止,其剩余进程将根据 KillMode=
设置终止(参见 systemd.kill(5)
)。如果未指定此选项,则会在请求停止服务时,通过发送 KillSignal=
或 RestartKillSignal=
中指定的信号来终止进程。支持指定符和环境变量替换(包括 $MAINPID
,见上文)。
请注意,在此设置中指定一条仅要求服务终止(例如,向其发送某种形式的终止信号)但不等待服务终止的命令通常是不够的。由于服务的剩余进程会在命令退出后立即根据 KillMode=
和 KillSignal=
或 RestartKillSignal=
被杀死,因此这可能不会导致干净利落的停止。因此,指定的命令应该是同步操作,而不是异步操作。
请注意,只有当服务首先成功启动时,才会执行 ExecStop=
中指定的命令。如果服务根本没有启动,或者启动失败,例如,由于 ExecStart=
、ExecStartPre=
或 ExecStartPost=
中指定的任何命令失败(且前缀没有"-",见上文)或超时,则不会调用这些命令。使用 ExecStopPost=
可在服务未能正确启动并再次关闭时调用命令。还要注意的是,如果服务成功启动,即使服务中的进程自行终止或被杀死,停止操作也始终会执行。必须准备好停止命令来处理这种情况。如果 systemd
知道主进程在调用停止命令时已经退出,那么 $MAINPID
将被取消设置。
服务重启请求的执行方式是,先执行停止操作,再执行启动操作。这意味着 ExecStop=
和 ExecStopPost=
会在服务重启操作期间执行。
建议将此设置用于与请求终止清理的服务通信的命令。对于死后清理步骤,请使用 ExecStopPost=
代替。
ExecStopPost=
服务停止后执行的其他命令。这包括使用 ExecStop=
中配置的命令、服务未定义任何 ExecStop=
或服务意外退出的情况。该参数可使用多条命令行,使用方法与 ExecStart=
相同。 这些设置的使用是可选的。支持指定符和环境变量替换。需要注意的是,与 ExecStop=
不同,使用此设置指定的命令会在服务未能正确启动并再次关闭时调用。
建议将此设置用于清理操作,这些操作即使在服务无法正常启动时也应执行。即使服务中途启动失败并留下未完全初始化的数据,使用此设置配置的命令也必须能够运行。由于在执行此设置指定的命令时,服务进程已经终止,因此它们不应试图与服务进程通信。
请注意,使用此设置配置的所有命令都是用服务的结果代码以及主进程的退出代码和状态调用的,这些代码和状态设置在$SERVICE_RESULT
, $EXIT_CODE
和$EXIT_STATUS
环境变量中,详细信息请参阅system .exec(5)
。
请注意,执行 ExecStopPost=
时会考虑 Before=/After=
排序限制。
RestartSec=
配置重启服务前的休眠时间(与 Restart=
配置相同)。以秒为单位的无单位值或时间跨度值,如 “5min 20s
”。默认值为 100ms
。
RestartSteps=
配置从 RestartSec=
到 RestartMaxDelaySec=
增加自动重启间隔的步数。 取正整数或 0
表示禁用。默认值为 0
。
此设置仅在RestartMaxDelaySec=
也设置时有效。
在254版添加。
RestartMaxDelaySec=
配置重启服务前的最长休眠时间,时间间隔随 RestartSteps=
的增加而增加。 取值格式与 RestartSec=
相同,或取 "infinity
"以禁用该设置。默认为 “infinity
”。
此设置仅在RestartSteps=
也设置时有效。
在254版添加。
TimeoutStartSec=
配置等待启动的时间。如果守护进程服务没有在配置的时间内发出启动完成的信号,则该服务将被视为失败,并将再次关闭。具体的操作取决于TimeoutStartFailureMode=
选项。接受以秒为单位的无单位值,或时间跨度值,如“5min 20s
”。传递"infinity
"来禁用超时逻辑。默认为DefaultTimeoutStartSec=
在管理器中设置,除非使用Type=oneshot
,在这种情况下,超时默认是禁用的(参见systemd-system.conf(5)
)。
如果 Type=notify/Type=notify-reload
服务发送 “EXTEND_TIMEOUT_USEC=...
”,则可能导致启动时间延长至超过 TimeoutStartSec=
。如果服务在指定的时间间隔内重复发送 “EXTEND_TIMEOUT_USEC=...
”,直到服务启动状态由 "READY=1
"结束,服务管理器将允许服务继续启动。(参见 sd_notify(3)
)。
在188版添加。
TimeoutStopSec=
该选项有两个作用。首先,它配置了每条 ExecStop=
命令的等待时间。如果其中任何一条命令超时,后续的 ExecStop=
命令将被跳过,服务将被 SIGTERM
终止。如果没有指定 ExecStop=
命令,服务将立即收到 SIGTERM
。这一默认行为可通过 TimeoutStopFailureMode=
选项更改。其次,它配置了服务本身停止的等待时间。如果服务没有在指定时间内终止,就会被 SIGKILL
强制终止(参见 systemd.kill(5)
中的 KillMode=
)。接受一个以秒为单位的无单位值,或一个时间跨度值,如 “5min 20s
”。输入 "infinity
"可禁用超时逻辑。默认为管理器配置文件中的 DefaultTimeoutStopSec=
(参见 systemd-system.conf(5)
)。
如果 Type=notify/Type=notify-reload
服务发送 “EXTEND_TIMEOUT_USEC=...
”,可能会导致停止时间超出 TimeoutStopSec=
。如果服务在指定的时间间隔内重复发送 “EXTEND_TIMEOUT_USEC=...
”,或自行终止(请参阅 sd_notify(3)
),服务管理器将允许服务继续停止。
在 188 版中添加。
TimeoutAbortSec=
该选项用于配置服务因看门狗超时而终止时的等待时间(请参阅 WatchdogSec=
)。如果服务的 TimeoutStopSec=
时间较短,则可使用此选项给系统更多时间写入服务的核心转储。过期后,服务将被 SIGKILL
强制终止(参见 systemd.kill(5)
中的 KillMode=
)。在这种情况下,核心文件将被截断。使用 TimeoutAbortSec=
为每个服务的核心转储设置合理的超时时间,该超时时间既要足够大以写入所有预期数据,又要足够短以适时处理服务故障。
接受以秒为单位的无单位值,或时间跨度值,如“5min 20s”。传递一个空值以跳过专用看门狗中止超时处理并回退TimeoutStopSec=
。传递"infinity
"来禁用超时逻辑。默认为管理器配置文件(参见 systemd-system.conf(5)
)中的 DefaultTimeoutAbortSec=
。
如果 Type=notify/Type=notify-reload
服务自行处理 SIGABRT
(而不是依赖内核写入内核转储),则可以发送 "EXTEND_TIMEOUT_USEC=...
"以将中止时间延长至超过 TimeoutAbortSec=
。如果服务在指定的时间间隔内重复发送 “EXTEND_TIMEOUT_USEC=...
”,或自行终止(请参阅 sd_notify(3)
),服务管理器将允许服务继续终止。
在243版添加。
TimeoutSec=
将 TimeoutStartSec=
和 TimeoutStopSec=
配置为指定值的快捷方式。
TimeoutStartFailureMode=, TimeoutStopFailureMode=
这些选项分别配置当守护进程服务在其配置的 TimeoutStartSec=
内未发出启动信号,或在 TimeoutStopSec=
内未停止时所采取的行动。 从 terminate
、abort
和 kill
中任选其一。两个选项的默认值都是terminate
。
如果设置了 terminate
,服务将通过发送 KillSignal=
中指定的信号(默认为 SIGTERM
,参见 systemd.kill(5)
)从容终止。如果服务没有终止,则会在 TimeoutStopSec=
之后发送 FinalKillSignal=
。 如果设置了 abort
,则会发送 WatchdogSignal=
,并在发送 FinalKillSignal=
之前应用 TimeoutAbortSec=
。 此设置可用于分析间歇性无法启动或关闭的服务。使用 kill
后,服务会通过发送 FinalKillSignal=
立即终止,而不会再有任何超时。此设置可用于加速关闭故障服务。
在246版添加。
RuntimeMaxSec=
配置服务运行的最大时间。如果使用了此方法,并且服务活动的时间超过了指定的时间,那么它将被终止并进入故障状态。请注意,此设置对Type=oneshot
服务没有任何影响,因为它们在激活完成后立即终止。传递"infinity
"(默认值)来配置没有运行时限制。
如果Type=notify/Type=notify-reload
的服务发送“EXTEND_TIMEOUT_USEC=…
”,这可能会导致运行时扩展到RuntimeMaxSec=
之外。该消息的第一次接收必须在超过RuntimeMaxSec=
之前发生,一旦运行时超出了RuntimeMaxSec=
,服务管理器将允许服务继续运行,前提是服务在指定的间隔内重复“EXTEND_TIMEOUT_USEC=…
”,直到通过“STOPPING==1”(或termination)实现服务关闭。(见sd_notify(3)
)。
在229版添加。
RuntimeRandomizedExtraSec=
此选项修改 RuntimeMaxSec=
,以 0 到指定值(单位:秒)之间均匀分布的持续时间来增加最大运行时间。如果未指定 RuntimeMaxSec=
,则将禁用此功能。
在 250 版中添加。
WatchdogSec=
配置服务的看门狗超时。看门狗在启动完成后被激活。服务必须在 "WATCHDOG=1
"的情况下定期调用 sd_notify(3)
(即 “keep-alive ping
”)。如果两次调用之间的间隔时间大于配置的时间,则服务将处于失败状态,并以 SIGABRT
(或 WatchdogSignal=
指定的信号)终止。将 Restart=
设置为 “on-failure
”、“on-watchdog
”、"on-abnormal
"或 “always
”,服务将自动重启。此处配置的时间将通过 WATCHDOG_USEC=
环境变量传递给被执行的服务进程。如果服务启用了看门狗支持,守护进程可通过此选项自动启用 keep-alive ping
逻辑。如果使用此选项,则应设置 NotifyAccess=
(见下文),以打开对 systemd
提供的通知套接字的访问。如果未设置 NotifyAccess=
,则将隐式设置为 main
。默认值为 0
,即禁用此功能。服务可以检查服务管理器是否期望收到看门狗保持更新通知。sd_event_set_watchdog(3)
可用于启用自动看门狗通知支持。
Restart=
配置服务进程退出、被杀死或超时时是否重新启动服务。服务进程可以是主服务进程,也可以是使用 ExecStartPre=
、ExecStartPost=
、ExecStop=
、ExecStopPost=
或 ExecReload=
指定的进程之一。 如果进程的死亡是 systemd
操作(如服务停止或重启)的结果,则不会重启服务。超时包括错过看门狗 "keep-alive ping
"截止时间,以及服务启动、重载和停止操作超时。
取no
、on-success
、on-failure
、on-abnormal
、on-watchdog
、on-abort
或always
中的一个值。如果设置为no
(默认值),则不会重新启动服务。如果设置为on-success
,则只有在服务进程干净退出时才会重启。在这种情况下,干净退出是指以下任何一种情况:
- 退出码为0;
- 对于
Type=oneshot
以外的类型,信号为SIGHUP
、SIGINT
、SIGTERM
或SIGPIPE
之一; - 在
SuccessExitStatus=
中指定退出状态和信号。
如果设置为 on-failure
,则当进程以非零退出码退出、被信号终止(包括内核转储,但不包括上述四个信号)、操作(如服务重载)超时或配置的看门狗超时被触发时,服务将被重启。如果设置为on-abnormal
,当进程被信号终止(包括内核转储,但不包括上述四个信号)、操作超时或看门狗超时触发时,服务将重新启动。如果设置为 on-abort
,则只有在服务进程因未捕获信号而退出(未指定为干净退出状态)时,才会重启服务。如果设置为 on-watchdog
,则只有在服务的看门狗超时到期时才会重启服务。如果设置为always
,则无论服务是否干净退出、被信号异常终止或超时,都将重新启动。
表 1. 退出原因和 Restart=
设置的影响
Restart设置 /退出原因 | no | always | on-success | on-failure | on-abnormal | on-abort | on-watchdog |
---|---|---|---|---|---|---|---|
干净的退出码或信号 | X | X | |||||
不干净的退出码 | X | X | |||||
不干净的信号 | X | X | X | X | |||
超时 | X | X | |||||
看门狗 | X | X | X |
作为上述设置的例外情况,如果在 RestartPreventExitStatus=
中指定了退出码或信号(见下文),或使用 systemctl stop
或类似操作停止了服务,则服务将不会重新启动。此外,如果在 RestartForceExitStatus=
中指定了退出码或信号(见下文),服务将始终重新启动。
注意服务重启受StartLimitIntervalSec=
和StartLimitBurst=
配置的单位启动速率限制,详见system .unit(5)
。
对于长时间运行的服务,建议将其设置为on-failure
,以便通过尝试从错误中自动恢复来提高可靠性。对于能够自行选择终止(并避免立即重新启动)的服务,on-abnormal
是另一种选择。
RestartMode=
接受一个字符串值,该值指定服务应该如何重启:
- 如果设置为
normal
(默认值),服务将通过failed/inactive
状态重新启动。 - 如果设置为
direct
,服务在自动重启时直接切换到activating
状态,跳过failed/inactive
状态。调用ExecStopPost=
。OnSuccess=
和OnFailure=
被跳过。
在依赖单元可能暂时失效,但我们不希望这些暂时失效导致依赖单元失效的情况下,该选项非常有用。当该选项设置为direct
时,依赖单元不会收到这些临时故障的通知。
在254版添加。
SuccessExitStatus=
接受一系列退出状态定义,当主服务进程返回这些定义时,除了正常的成功退出状态0以及对于Type=oneshot
以外的信号SIGHUP
、SIGINT
、SIGTERM
和SIGPIPE
,它们也会被认为成功的终止。退出状态定义可以是数字终止状态、终止状态名称或终止信号名称,用空格分隔。参见systemd.exec(5)
中的进程退出码部分,了解终止状态名称的列表(对于此设置,只应使用不带"EXIT_"或"EX_"前缀的部分)。
参见signal(7)
了解信号名称的列表。
注意,此设置不会改变数字退出状态与其名称之间的映射,即无论如何使用此设置,0仍将映射为"SUCCESS
"(因此通常在工具输出中显示为"0/SUCCESS
"),1映射为"FAILURE
"(因此通常显示为"1/FAILURE
"),等等。它只控制这些退出状态的效果,以及它们如何传播到整个服务的状态。
此选项可以出现多次,此时成功的退出状态列表将被合并。如果将空字符串分配给此选项,列表将被重置,所有先前的此选项的分配将无效。
示例1. 设置SuccessExitStatus=
的服务
SuccessExitStatus=TEMPFAIL 250 SIGKILL
退出状态75(TEMPFAI
L)、250
和终止信号SIGKILL
被认为是干净的服务终止。
注意:systemd-analyze exit-status
可用于列出退出状态并翻译数字状态值和名称。
在189版添加。
RestartPreventExitStatus=
接受一系列退出状态定义,当主服务进程返回这些定义时,将阻止自动服务重启,无论使用Restart=配置的重启设置如何。退出状态定义可以是数字退出代码或终止信号名称,并用空格分隔。默认为空列表,因此默认情况下,没有退出状态被排除在配置的重启逻辑之外。
例如:
RestartPreventExitStatus=1 6 SIGABRT
确保退出码1和6以及终止信号SIGABRT
不会导致服务自动重启。此选项可以出现多次,此时阻止重启的状态列表将被合并。如果将空字符串分配给此选项,列表将被重置,所有先前的此选项的分配将无效。
注意:此设置对于通过ExecStartPre=
、ExecStartPost=
、ExecStop=
、ExecStopPost=
或ExecReload=
配置的进程没有影响,仅对主服务进程有效,即由ExecStart=
调用的进程或(根据Type=
、PIDFile=、...
)其他配置的主进程。
在189版添加。
RestartForceExitStatus=
接受一系列退出状态定义,当主服务进程返回这些定义时, 将强制自动服务重启,无论使用Restart=
配置的重启设置如何。参数格式与RestartPreventExitStatus=
类似。
在215版添加。
RootDirectoryStartOnly=
接受一个布尔参数。如果为真,仅将RootDirectory=
选项(参见systemd.exec(5)
获取更多信息)配置的根目录应用于ExecStart=
启动的进程,而不应用于其他各种ExecStartPre=
、ExecStartPost=
、ExecReload=
、ExecStop=
和ExecStopPost=
命令。如果为假,该设置以相同的方式应用于所有配置的命令。默认为假。
NonBlocking=
为通过基于套接字的激活传递的所有文件描述符设置O_NONBLOCK
标志。如果为真,所有文件描述符>=3(即除了stdin
、stdout
、stderr
之外的所有文件描述符),不包括通过文件描述符存储逻辑传递的文件描述符(有关详细信息,参见FileDescriptorStoreMax=
), 将设置O_NONBLOCK
标志,因此处于非阻塞模式。
此选项仅与套接字单元结合使用时有用,如在systemd.socket(5
)中所述,并且对之前保存在文件描述符存储中的文件描述符没有影响。
注意,如果相同的套接字单元配置为传递给多个服务单元(通过Sockets=
设置,见下文),并且这些服务具有不同的NonBlocking=
配置,那么O_NONBLOCK
的确切状态取决于这些服务被调用的顺序,并且在服务代码已经获取套接字文件描述符之后可能会改变,仅仅因为套接字的O_NONBLOCK
状态由引用它的所有文件描述符共享。因此,所有共享相同套接字的服务必须使用相同的NonBlocking=
配置,并且不在服务代码中更改标志。
NotifyAccess=
控制对服务状态通知套接字的访问,该套接字可通过sd_notify(3)
调用访问。接受none
(默认)、main
、exec
或all
之一。如果为none
,则不接受来自服务进程的任何守护进程状态更新,所有状态更新消息都被忽略。
如果为main
,仅接受来自服务的主进程发送的服务更新。如果为exec
,仅接受来自Exec*=
命令之一的主进程或控制进程发送的服务更新。如果为all
,接受服务控制组所有成员的所有服务更新。此选项应在使用Type=notify/Type=notify-reload
或WatchdogSec=
时设置为打开通知套接字的访问(见上文)。如果使用这些选项但没有配置NotifyAccess=
,它将被隐式设置为main
。
注意,只有在发送进程在PID
1处理消息时仍然存在,或者发送进程被服务管理器明确运行时跟踪时,
sd_notify()
通知才可能正确地归因于单元。后者是服务管理器最初派生进程的情况,即与main
或exec
匹配的所有进程。相反,如果单元的辅助进程发送sd_notify()消息然后立即退出,服务管理器可能无法正确地将消息归因于单元,因此将忽略它,即使NotifyAccess=all
已为其设置。
因此,为了消除涉及查找客户端单元和正确归因于单元的所有竞争条件,可以使用sd_notify_barrier()
。
此调用充当同步点,并确保在此调用成功返回之前发送的所有通知都已被服务管理器接收。使用sd_notify_barrier()
是必要的,对于不是由服务管理器调用的客户端,否则这种同步机制对于将通知归因于单元是不必要的。
Sockets=
指定此服务在启动时应从哪个套接字单元继承套接字文件描述符的名称。通常,不应使用此设置,因为所有套接字文件描述符的单元与服务名称相同(当然要受到不同单元名称后缀的限制)都会传递给生成的进程。
请注意,同一个套接字文件描述符可能会同时传递给多个进程。还要注意的是,在接收到套接字流量时,可能会激活与最终配置为继承套接字文件描述符的服务不同的服务。或者换句话说:.socket
单元的 Service=
设置不必与它所指向的 .service
的 Sockets=
设置相对应。
此选项可以出现多次,此时套接字单元列表将被合并。请注意,一旦设置,再次清除套接字列表(例如,通过将空字符串分配给此选项)是不支持的。
FileDescriptorStoreMax=
配置服务管理器为使用sd_pid_notify_with_fds(3)
的"FDSTORE=1"
消息的服务存储多少文件描述符。 这对于实现可以在明确请求或崩溃后重启而不丢失状态的服务非常有用。 任何在重启期间不应关闭的打开套接字和其他文件描述符都可以这样存储。应用程序状态可以序列化到RuntimeDirectory=
中的文件中, 或者存储在memfd_create(2)
内存文件描述符中。默认为0,即不允许在服务管理器中存储文件描述符。 从特定服务传递给服务管理器的所有文件描述符在下一次服务重启时都会传回给服务的主进程(有关使用的确切协议和传递文件描述符的顺序的详细信息,参见sd_listen_fds(3)
)。 任何传递给服务管理器的文件描述符在看到POLLHUP
或POLLERR
时都会自动关闭,或者当服务完全停止并且没有作业排队或正在执行时(后者可以通过FileDescriptorStorePreserve=
进行调整, 见下文)。如果使用此选项,NotifyAccess=
(见上文)应设置为打开对systemd
提供的套接字的通知访问。 如果未设置NotifyAccess=
,它将被隐式设置为main
。
可以使用systemd-analyze(1
)的fdstore
命令列出服务的文件描述符存储的当前内容。
注意,服务管理器只会将文件描述符存储中的文件描述符传递给服务自己的进程,永远不会通过IPC
或其他方式传递给其他客户端。 但是,它允许非特权客户端查询当前打开的服务文件描述符列表。因此,可以将敏感数据安全地放置在引用的文件中, 但不应附加到存储的文件描述符的元数据中(例如包含在文件名中)。
如果将此选项设置为非零值,则为为此服务调用的进程设置$FDSTORE
环境变量。有关详细信息,请参阅systemd.exec(5)
。
有关文件描述符存储的更多信息,请参见文件描述符存储[1]概述。
在219版添加。
FileDescriptorStorePreserve=
接受no
、yes
、restart
之一,并控制何时释放服务的文件描述符存储(即何时关闭包含的文件描述符,如果有)。 如果设置为no
,文件描述符存储在服务停止时自动释放;如果设置为restart
(默认值),只要单元不是非活动状态或失败状态,或者为服务排队了作业, 或者预计要重启服务,就会保留它。如果设置为yes
,文件描述符存储会保留,直到单元从内存中移除(即不再被引用并且处于非活动状态)。后者有助于将文件描述符存储中的条目固定,直到服务管理器退出。
使用systemctl clean --what=fdstore ...
显式释放文件描述符存储。
在254版添加。
USBFunctionDescriptors=
配置包含USB FunctionFS[2]
描述符的文件位置,用于实现USB gadget
功能。仅与配置了ListenUSBFunction=
的套接字单元一起使用。打开后,此文件的内容将写入ep0
文件。
在227版添加。
USBFunctionStrings=
配置包含USB FunctionFS
字符串的文件位置。行为与USBFunctionDescriptors=
类似。
在227版添加。
OOMPolicy=
为内核和用户空间OOM
杀手systemd-oomd.service
(8)配置内存不足(OOM)杀死策略。在Linux中,当内存稀缺到内核难以为其自身分配内存时,它可能会决定杀死正在运行的进程以释放内存并减轻内存压力。请注意,systemd-oomd.service
是一个更灵活的解决方案,旨在通过尽早终止服务来防止用户空间出现内存不足的情况,而不仅仅是内核,在内核必须采取行动之前。
此设置接受continue
、stop
或kill
之一。如果设置为continue
并且单元中的进程被OOM杀手杀死,这将被记录,但单元继续运行。如果设置为stop
,事件将被记录,但服务管理器将通过服务管理器干净地终止单元。如果设置为kill
并且单元的一个进程被OOM
杀手杀死,内核将被指示杀死单元的所有剩余进程,通过将memory.oom.group
属性设置为1;另见内核页面控制组v2[3]。
默认为systemd-system.conf(5)
中设置的DefaultOOMPolicy=
,除了Delegate=
已打开的单元,其中默认为continue
。使用OOMScoreAdjust=
设置来配置单元的进程是否应被视为Linux OOM杀手逻辑的首选或较不首选的进程终止候选者。有关详细信息,请参阅systemd.exec(5)。
此设置也适用于systemd-oomd.service(8)
。类似于内核执行的OOM杀死,此设置决定了systemd-oomd
杀死与其关联的cgroup
后单元的状态。`
在243版添加。
OpenFile=
接受"路径[:fd名称:选项]"形式的参数,其中:
- "路径"是文件系统中的文件或AF_UNIX套接字的路径;
- “
fd名称
"是将与文件描述符关联的名称;名称可以包含任何ASCII字符,但必须排除控制字符和”:",并且长度最多为255个字符;它是可选的,如果不提供,默认为文件名; - “选项"是访问选项的逗号分隔列表;可能的值为"只读”、“追加”、“截断”、“优雅”;如果未指定,文件将以
rw
模式打开;如果指定了"优雅",在打开文件/套接字期间的错误将被忽略。多次指定相同的选项将被视为错误。
服务管理器打开文件或套接字,并将文件描述符传递给服务。如果路径是套接字,我们将在其上调用connect()
。有关如何检索这些文件描述符的更多详细信息,请参阅sd_listen_fds(3)
。
此设置有助于允许服务访问它们自己无法访问的文件/套接字(由于在单独的挂载命名空间中运行、没有权限等)。
此设置可以多次指定,在这种情况下,所有指定的路径都将被打开,并将文件描述符传递给服务。如果分配了空字符串,则重置此之前定义的所有打开文件的整个列表。
在253版添加。
ReloadSignal=
配置在要求重新加载服务配置时发送给服务主进程的UNIX进程信号。默认为SIGHUP
。
除非使用Type=notify-reload
,否则此选项无效,见上文。
在253版添加。
有关更多设置,请查看systemd.unit(5)
、systemd.exec(5)
和systemd.kill(5)
。
命令行
本节介绍 ExecStart=
、ExecStartPre=
、ExecStartPost=
、ExecReload=
、ExecStop=
和 ExecStopPost=
选项的命令行解析以及变量和指定符替换。
可以用分号将多个命令行连接到一个指令中(这些分号必须作为单独的单词传递)。单独的分号可以转义为"\;
"。
每条命令行都使用 systemd.syntax(7
) 中 "引用 "部分描述的规则取消引用。第一项是要执行的命令,后面的项是参数。
该语法受到 shell 语法的启发,但只理解以下段落中描述的元字符和扩展,变量的扩展也有所不同。具体来说,不支持使用"<“、”<<“、”>“和”>>“的重定向、使用”|“的管道、使用”&"的后台运行程序以及 shell 语法的其他元素。
要执行的命令可以包含空格,但不允许包含控制字符。
每个命令可以用一些特殊字符作为前缀:
表 2. 特殊可执行文件前缀
前缀 | 效果 |
---|---|
“@” | 如果可执行路径以"@"为前缀,则第二个指定的标记将作为 argv[0] 传递给执行进程(而不是实际文件名),然后是指定的其他参数。 |
“-” | 如果可执行路径前缀为"-",则会记录通常被视为失败的命令的退出码(即非零退出状态或因信号导致的异常退出),但没有进一步影响,并被视为等同于成功 |
“:” | 如果可执行路径前缀为":",则不应用环境变量替换(如下文 "命令行 "部分所述)。 |
“+” | 如果可执行路径前缀为 “+”,则进程以完全权限执行。在此模式下,使用 User= 、Group= 、CapabilityBoundingSet= 或各种文件系统命名选项(如 PrivateDevices= 、PrivateTmp= )配置的权限限制不会应用于调用的命令行(但仍会影响其他任何 ExecStart= 、ExecStop=... 行)。但请注意,这不会绕过适用于整个控制组的选项,如 DevicePolicy= ,完整列表请参见 systemd.resource-control(5) 。 |
“!” | 与上面讨论的“+”字符类似,这允许以提升的权限调用命令行。但是,与“+”不同的是,“!”字符只会改变User= 、Group= 和supplements groups = 的效果,即只会影响用户和组凭证的节。注意,此设置可以与DynamicUser= 结合使用,在这种情况下,在调用命令之前分配动态用户/组对,但凭证更改留给执行的进程本身。 |
“!!” | 该前缀与"!“非常相似,但只对不支持环境进程能力的系统有效,即不支持 AmbientCapabilities= 。 该前缀旨在用于利用环境能力的单元文件,以便尽可能以最小权限运行进程,同时与不支持环境能力的系统保持兼容。需要注意的是,当使用”!!“并检测到系统缺乏环境能力支持时,任何已配置的 SystemCallFilter= 和 CapabilityBoundingSet= 字节都会被隐式修改,以允许生成的进程自行放弃凭证和能力,即使已配置为不允许这样做。此外,如果使用了该前缀,但检测到系统不支持环境能力,那么 AmbientCapabilities= 将被跳过,不会被应用。在支持环境能力的系统中,”!!"没有任何作用,是多余的。 |
@“、”-“、”: “和 “+”/”!“/”!!“中的一个可以同时使用,且可以任意顺序出现。但是,一次只能使用 “+”、”!“、”!!"中的一个。
对于每条命令,第一个参数必须是可执行文件的绝对路径或不含斜线的简单文件名。如果命令不是完整(绝对)路径,则将使用编译时确定的固定搜索路径解析为完整路径。搜索的目录包括/usr/local/bin/
、/usr/bin/
、使用分割/usr/bin/
和/bin/
目录的系统中的/bin/
,以及使用分割 bin/
和 sbin/
目录的系统中的对应 sbin/
。因此,对于位于任何 "标准 "目录下的可执行文件,只使用可执行文件名都是安全的,而在其他情况下则必须使用绝对路径。提示:可以使用 systemd-path search-binaries-default
查询该搜索路径。
命令行接受system .unit(5)
中描述的“%
”说明符。
支持基本的环境变量替换。在命令行中将"${FOO}
“作为一个词的一部分或作为一个单独的词使用,在这种情况下,”${FOO}
“将被删除并替换为环境变量的精确值(如果有的话),包括其中包含的所有空格,结果总是只有一个参数。在命令行中将”$FOO "作为一个单独的单词使用,在这种情况下,该单词将被环境变量的值所取代,并在空白处分隔,从而产生零个或多个参数。对于这种类型的扩展,在分割成单词时会使用引号,之后会移除引号。
例如:
Environment="ONE=one" 'TWO=two two'
ExecStart=echo $ONE $TWO ${TWO}
这将使用四个参数执行/bin/echo
: “one
”, “two
”,“two
"和"two two
”。
例如:
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 命令行。如果要使用 shell 命令行,需要将其明确传递给某种 shell 实现。例如
ExecStart=sh -c 'dmesg | tac'
例如:
ExecStart=echo one ; echo "two two"
这会执行两次 echo
,每次使用一个参数: 分别为 "one
"和 “two two
”。由于指定了两条命令,因此必须使用 Type=oneshot
。
例如:
Type=oneshot
ExecStart=:echo $USER ; -false ; +:@true $TEST
这将执行带有字面参数"$USER
“的 /usr/bin/echo
(”: “抑制变量扩展),然后执行 /usr/bin/false
(返回值将被忽略,因为”-
“抑制对返回值的检查)和 /usr/bin/true
(使用提升的权限,以”$TEST
"作为 argv[0]
)。
例如:
ExecStart=echo / >/dev/null & \; \
ls
这将使用五个参数执行echo: “/
”, ">/dev/null
",“&
”, ";
“和"ls
”。
示例
示例 2. 简单服务
下面的单元文件创建了一个执行 /usr/sbin/foo-daemon
的服务。由于未指定 Type=
,因此将默认使用 Type=simple
。systemd
将假定该单元在程序开始执行后立即启动。
[Unit]
Description=Foo
[Service]
ExecStart=/usr/sbin/foo-daemon
[Install]
WantedBy=multi-user.target
请注意,systemd
在此假定 systemd
启动的进程将继续运行,直至服务终止。如果程序自我守护(即派生),请使用 Type=forking
代替。
由于没有指定 ExecStop=
,systemd
会向所有从该服务启动的进程发送 SIGTERM
,超时后还会发送 SIGKILL
。这一行为可以修改,详情请参见 systemd.kill(5)
。
请注意,当服务完成初始化时,此单元类型不包含任何类型的通知。为此,您应该使用其他单元类型,例如Type=notify/Type=notify-reload
(如果服务理解systemd
的通知协议),Type=forking
(如果服务可以后台运行),Type=dbus
(如果单元在初始化完成后获得了一个dbus
名称)。见下文。
示例 3. 即时服务
有时,单元只需执行一项操作,而无需保留活动进程,例如文件系统检查或启动时的清理操作。为此,Type=oneshot
应运而生。这种类型的单元将等待指定进程终止,然后恢复为非活动状态。以下单元将执行清理操作:
[Unit]
Description=Cleanup old Foo data
[Service]
Type=oneshot
ExecStart=/usr/sbin/foo-cleanup
[Install]
WantedBy=multi-user.target
请注意,在程序结束之前,systemd
会将单元视为处于 "starting
"状态,因此有序的依赖项会等待程序结束后才启动自己。执行结束后,单元将恢复到 "inactive
"状态,永远不会达到 "active
"状态。这意味着再次请求启动单元将再次执行该操作。
Type=oneshot
是唯一可以指定多个 ExecStart=
的服务单元。对于有多个命令的单元(Type=oneshot
),所有命令都将再次运行。
对于Type=oneshot
,不允许使用Restart=always
和Restart=on-success
。
示例 4. 可停止的即时服务
与 oneshot
服务类似,有时有些单元需要执行一个程序来设置某些东西,然后再执行另一个程序来关闭它,但在它们被视为 "started
"时,没有进程保持活动状态。网络配置有时就属于这种情况。另一种用例是,如果oneshot
启动服务作为依赖项被拉入时,不应该每次都执行,而应该只执行第一次。
为此,systemd
知道 RemainAfterExit=yes
设置,如果启动操作成功退出,systemd
就会认为单元处于活动状态。该指令可用于所有类型,但在 Type=oneshot
和 Type=simple
时最有用。在 Type=oneshot
时,systemd
会等到启动操作完成后才认为单元处于活动状态,因此只有在启动操作成功后,依赖关系才会启动。当 Type=simple
时,依赖关系将在启动操作发出后立即启动。下面的单元提供了一个简单静态防火墙的示例。
[Unit]
Description=Simple firewall
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/simple-firewall-start
ExecStop=/usr/local/sbin/simple-firewall-stop
[Install]
WantedBy=multi-user.target
由于单元在启动操作退出后被视为正在运行,因此再次调用 systemctl start
将不会对该单元采取任何操作。
示例5.传统的forking服务
许多传统的守护进程/服务在启动时都会自我后台化(即 fork
、daemonize
)。在服务的单元文件中设置 Type=forking
,以支持这种运行模式。当原程序仍在运行时,systemd
将认为服务处于初始化过程中。一旦成功退出,且至少还有一个进程存在(RemainAfterExit=no
),服务即被视为已启动。
传统的守护进程通常只由一个进程组成。因此,如果原始进程终止后只剩下一个进程,systemd
会将该进程视为服务的主进程。在这种情况下,$MAINPID
变量将在 ExecReload=
和 ExecStop=
等命令中可用。
如果存在多个进程,systemd
将无法确定主进程,因此不会假定存在主进程。在这种情况下,$MAINPID
将不会扩展为任何内容。不过,如果进程决定写入一个传统的 PID
文件,systemd
将能从该文件中读取主进程的 PID
。请相应设置 PIDFile=
。请注意,守护进程应在完成初始化之前写入该文件。否则,systemd
可能会在文件存在之前就尝试读取它。
下面的示例展示了一个简单的守护进程,它在后台派生并只启动一个进程:
[Unit]
Description=Some simple daemon
[Service]
Type=forking
ExecStart=/usr/sbin/my-simple-daemon -d
[Install]
WantedBy=multi-user.target
请参阅 systemd.kill(5)
,了解如何影响 systemd
终止服务的方式。
示例 6. DBus 服务
对于在 DBus
系统总线上获取名称的服务,请使用 Type=dbus
并相应设置 BusName=
。一旦在系统总线上获得名称,systemd
将认为服务已初始化。下面的示例展示了一个典型的 DBus
服务:
[Unit]
Description=Simple DBus service
[Service]
Type=dbus
BusName=org.example.simple-dbus-service
ExecStart=/usr/sbin/simple-dbus-service
对于总线可激活服务,请勿在 systemd
服务文件中包含 [Install]
部分,而是在相应的 DBus
服务文件中使用 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)
,了解如何影响 systemd
终止服务的方式。
示例7.通知systemd其初始化的服务
Type=simple
服务非常容易编写,但它的主要缺点是 systemd
无法知道指定服务的初始化何时完成。因此,systemd
支持一个简单的通知协议,允许守护进程通知 systemd
它们已完成初始化。为此,请使用 Type=notify
或 Type=notify-reload
。此类守护进程的典型服务文件如下所示:
[Unit]
Description=Simple notifying service
[Service]
Type=notify
ExecStart=/usr/sbin/simple-notifying-service
[Install]
WantedBy=multi-user.target
请注意,守护进程必须支持 systemd
的通知协议,否则 systemd
会认为服务尚未启动,并在超时后将其杀死。关于如何以透明方式更新守护进程以支持该协议,请参阅 sd_notify(3)
。 systemd
会认为服务处于 "starting "状态,直到收到准备就绪通知。
请参阅 systemd.kill(5)
,了解如何影响 systemd
终止服务的方式。
另请参阅
systemd(1), systemctl(1), systemd-system.conf(5),systemd.unit(5), systemd.exec(5), systemd.resource-control(5),systemd.kill(5), systemd.directives(7), systemd-run(1)
说明
- File Descriptor Store
https://systemd.io/FILE_DESCRIPTOR_STORE - USB FunctionFS
https://docs.kernel.org/usb/functionfs.html - Control Group v2
https://docs.kernel.org/admin-guide/cgroup-v2.html