文件路径与名字

在开始编写配置文件前,我们需要先了解一下一些设定.

文件存放路径

Systemd 从一下地方读取配置文件:

  • /usr/lib/systemd/system/: 存放绝大部分配置文件.
  • /etc/systemd/system/: systemd 启动时会默认执行的路径.
  • /usr/lib/sysemd/user/:
  • ~/.local/share/systemd/user/
  • /etc/systemd/system/user:
  • ~/.config/systemd/user/

名字约定

配置文件名字只能由 ASCII 字母,下划线和点组成.

末尾带有 @的是模板服务,子服务通过在 @后添加内容作为值, 将模板服务实例化。在模板中 代表那个值的字符为 % i.

编辑文件的道道

因为包配置文件会经常更新,所以直接修改包文件是个很蠢的办法.

为了能一劳永逸地完成修改,我们有两种方式:

  • 在另一个地方覆盖原来的文件
    使用 systemctl edit --full 在 /etc/systemd/system/ 中创建或打开对应的配置文件. 如果设置了默认启动, 修改完后使用 systemctl reenable 重新链接新文件.
  • 使用附加文件。它们会附加到原文件的首部. 使用 systemctl edit

编辑完后,相关服务必须重启才能让修改生效. 如果通过 systemctl edit 修改,服务会自 动重启.

或者直接粗暴一点, systemctl daemon-reload 重启所有单元.

提示:

  • 可以利用 systemd-delta 查看所有被覆盖和修改过的文件.
  • 可以使用 systemctl cat 查看附加了附加文件的配置文件.

配置文件的编写

Unit

描述: Description

定义这个服务的综合描述.

文档: Documentation

定义文档的位置,如 man:sshd(8) man:sshd_config(5).

依赖: Wants 和 Requires

Sytemd 提供 2 种引入依赖的方式:

  • Wants= 所依赖的服务名:这种依赖是可选的, 即使所依赖的服务启动失败,父服务 也应该能正常执行.
  • Requires= 所依赖的服务名:这种依赖是强制的.

执行顺序: After 与 Before

注意,上面两种语句都不会强制要求子服务在父服务之前启动, 要强制做到这一点,请使用 after=要先启动的服务. 否则,这些服务可能干脆并行启动. 相应地还有一个 Before 变量定义该服务在哪些服务前启动.

多个服务间用空格简单分开就好.

冲突避免: Conflicts

定义会与该服务冲突的服务 (组), 当变量里的服务 (组) 在运行时, 该服务不能运行.

Service

环境变量文件: EnvironmentFile

定义当前服务的环境参数文件路径.

参数文件采用键=值的格式.

可以用 $键在配置文件中引用.

服务类型: Type

Systemd 提供多种类型的服务满足不同场景下的使用. 服务类型通过 Service 一节的变量 Type 决定:

  • simple: 默认值. Systemd 会立刻启动该服务. ExecStart 在主进程中执行命令. 如果需要使用别的服务, 不要用这个类型.
    • idle: 基本同 simple, 只是 systemd 会在其他服务都被调度妥当后才会执行. 一种场合是要让该服务的输出与其它服务分离开来.
    • notify: 基本与 simple 相同。只是服务保证会适时地向 systemd 发信号.
    • dbug: 类似 simple, 但是会等待 DBus 信号后启动.
  • forking: ExecStart 变量将会以分支进程的形式执行.
  • oneshot: 执行无死循环的程序并马上退出.

执行命令: Exec*

这个变量家族决定在什么时候执行什么样的命令, 在赋值符号后加一个 "-" 可以关闭错误输 出.

  • ExecStart: 启动服务时执行的命令.
  • ExecReload: 重启服务时执行的命令.
  • ExecStop: 停止服务时执行的命令.
  • ExecStartPre: 启动服务前执行的命令.
  • ExecStartPost: 启动服务后执行的命令.
  • ExecStopPost: 停止服务后执行的命令.

终结方式: KillMode

定义 Systemd 如何停止服务.

  • control-group: 杀死当前控制组里的所有进程.
  • process: 只杀死主进程. 经常被 sshd 等不希望停止服务后连接全断的服务使用.
  • mixed: 主进程收到 SIGTREM, 子进程收到 SIGKILL.
  • none: 执行 ExecStop

重启相关: Restart (Sec)

Restart 定义退出后的重启方式.

RestartSec 定义重启前等待的秒数.

  • no: 不重启
  • on-success: 仅在正常退出后重启.
  • on-failure: 仅在异常退出后重启。守护进程常用.
  • on-adnormal: 仅在收到终止信号或超时后才重启.
  • on-abort: 仅在被未捕获信号终止后重启.
  • on-watchdog: 仅在超时退出后重启.
  • always: 总是重启.

执行者: User 和 Group

Install

划定服务组: WantedBy

定义该服务所在的服务组.


服务组

服务组表示一组服务,

服务组有单独的文件。配置上基本与服务无异。后缀名是 target.

利用 systemd get-default 可以查看默认启动的服务组.

利用 systemctl list-dependencies 可以查看某个服务或服务组的包含的服务.

利用 systemctl isolate 可以切换到另一个服务组.

常用的服务组为 multi-user.target 和 graphical.target, 后者依赖前者.

定时器

就像服务组一样,定时器也有自己的文件。后缀名是 timer.

文件配置也基本与普通服务相同。最大的不同是多了一节 Timer.

定时器的文件名字最好与一个已经存在服务的文件名字相同, 代表这个定时器管理那个服务。当然, 我们也可以在 timer 一节中添加 Unit 变量指定一个名字不同的服务.

被管理的服务这下就不需要 Install 了,因为定时器会帮它照料好.

概括说来,有两种定时器: + 实时定时器: 使用变量 OnCalendar. 在某一特定日期触发.
格式为 DayOfWeek Year-Month-Day Hour:Minute:Second, "*" 代表任意值, 用 "上界.. 下界" 代表一个区间,用 "," 连接多个可能值.
当然,大家都懒,像 "daily", "weekly" 这样的简写也可以用.
想要知道自己时间究竟写对没有了吗? 运行 systemd-analyze calendar 时间就可以知道下次 触发的时间了.
定时器只会在它启动的状态下触发。要是该触发的时候没有启动, 那么这次触发就丢失了。将 Persistent 变量赋值为真, 定时器在下次启动时就会补上遗漏的触发. + 单调定时器: 使用变量家族 On类型Sec, 在某时间发生了一个时间段后触发. 电脑关机 或休眠的时间不计入.
类型可以是: + Boot + UnitActive + StartUp + Active + UnitInactive

要是担心多个任务同时唤醒会使得系统一下子变得很卡, RandomizeDelaySec 可以在预订 时间上加一个不超过所给值的延迟.

临时定时器

除了大动干戈的创建两个文件, systemd 事实上提供了一种更为方便的临时定时器.

systemd-run --on-active="时间点或时间段" 命令及参数......

systemd-run --on-active="时间点或时间段" --unit 服务名

以上两种方式会临时性的创建一个定时器运行命令或服务.