编写单元文件

systemd 单元文件的语法来源于 XDG 桌面项配置文件.desktop文件,最初的源头则是Microsoft Windows的.ini文件。单元文件可以从多个地方加载,systemctl show —property=UnitPath 可以按优先级从低到高显示加载目录:

  • /usr/lib/systemd/system/ :软件包安装的单元
  • /etc/systemd/system/ :系统管理员安装的单元
    注意:
  • systemd 运行在用户模式下时,使用的加载路径是完全不同的。
  • systemd 单元名仅能包含 ASCII 字符,下划线和点号和有特殊意义的字符('@', '-')。其它字符需要用 C-style "\x2d" 替换。参阅 systemd.unit(5)systemd-escape(1)

单元文件的语法,可以参考系统已经安装的单元,也可以参考 systemd.service(5) 中的EXAMPLES章节

提示: # 开头的注释可能也能用在 unit-files 中,但是只能在新行中使用。不要在 systemd 的参数后面使用行末注释, 否则 unit 将会启动失败。

处理依赖关系

使用 systemd 时,可通过正确编写单元配置文件来解决其依赖关系。典型的情况是,单元 A 要求单元 BA 启动之前运行。在此情况下,向单元 A 配置文件中的 [Unit] 段添加 Requires=BAfter=B 即可。若此依赖关系是可选的,可添加 Wants=BAfter=B 。请注意 Wants=Requires= 并不意味着 After= ,即如果 After= 选项没有制定,这两个单元将被并行启动。

依赖关系通常被用在服务(service)而不是目标(target)上。例如, network.target 一般会被某个配置网络接口的服务引入,所以,将自定义的单元排在该服务之后即可,因为 network.target 已经启动。

服务类型

编写自定义的 service 文件时,可以选择几种不同的服务启动方式。启动方式可通过配置文件 [Service] 段中的 Type= 参数进行设置。

  • Type=simple :(默认值) systemd认为该服务将立即启动。服务进程不会 fork 。如果该服务要启动其他服务,不要使用此类型启动,除非该服务是socket 激活型。
  • Type=forking :systemd认为当该服务进程fork,且父进程退出后服务启动成功。对于常规的守护进程(daemon),除非你确定此启动方式无法满足需求,使用此类型启动即可。使用此启动类型应同时指定 PIDFile=,以便 systemd 能够跟踪服务的主进程。
  • Type=oneshot :这一选项适用于只执行一项任务、随后立即退出的服务。可能需要同时设置 RemainAfterExit=yes 使得 systemd 在服务进程退出之后仍然认为服务处于激活状态。
  • Type=notify :与 Type=simple 相同,但约定服务会在就绪后向 systemd 发送一个信号。这一通知的实现由 libsystemd-daemon.so 提供。
  • Type=dbus :若以此方式启动,当指定的 BusName 出现在DBus系统总线上时,systemd认为服务就绪。
  • Type=idlesystemd会等待所有任务处理完成后,才开始执行 idle 类型的单元。其他行为与 Type=simple 类似。
    type 的更多解释可以参考 systemd.service(5)

修改现存单元文件

为了避免和 pacman 冲突,不应该直接编辑软件包提供的文件。有两种方法可以不改动原始文件就做到修改单元文件:创建一个优先级更高的本地单元文件或创建一个片段,应用到原始单元文件之上。两种方法都需要在修改后重新加载单元,用 systemctl edit 编辑单元(会自动重载单元)或通过下面命令重新加载单元:

  1. # systemctl daemon-reload

提示:

  • systemd-delta 命令用来查看哪些单元文件被覆盖、哪些被修改。系统维护的时候需要及时了解哪些单元已经有了更新。
  • 使用 systemctl cat unit 可以查看单元的内容和所有相关的片段.

替换单元文件

要替换 /usr/lib/systemd/system/unit, 创建文件 /etc/systemd/system/unit 并重新启用单元以更新链接:

  1. # systemctl reenable unit

或者运行:

  1. # systemctl edit --full unit

这将会在记事本中打开 /etc/systemd/system/unit,如果文件不存在,可以将安装的版本复制到这里,在编辑完成之后,会自动加载新版本。

注意: 即使 Pacman 更新了新的单元文件,软件包中的版本也不会被使用,所以这个方式会增加系统维护的难度,推荐使用下面一种方法。

附加配置片段

要附加配置片段,先创建名为 /etc/systemd/system/<单元名>.d/ 的目录,然后放入 *.conf 文件,其中可以添加或重置参数。这里设置的参数优先级高于原来的单元文件。下面的更新方式比较简单:

  1. # systemctl edit unit

这将会在编辑器中打开文件 /etc/systemd/system/unit.d/override.conf,编辑完成之后自动加载。

Note: 并不是所有参数都会被子配置文件覆盖。例如要修改 Conflicts= 就必须 替换原始文件

重置到软件包版本

要回退单元的变更,使用 systemctl edit 并执行:

  1. # systemctl revert unit

示例

例如,如果想添加一个额外的依赖,创建如下文件即可:

  1. /etc/systemd/system/<unit>.d/customdependency.conf
  1. [Unit]
  2. Requires=<新依赖>
  3. After=<新依赖>

要修改一个非 oneshot 单元的 ExecStart 命令,创建下面文件:

  1. /etc/systemd/system/unit.d/customexec.conf
  1. [Service]
  2. ExecStart=
  3. ExecStart=new command

修改 ExecStart 前必须将其置空,参见 ([1] 。所有可能多次赋值的变量都需要这个操作,例如定时器的 OnCalendar

下面是自动重启服务的一个例子:

  1. /etc/systemd/system/unit.d/restart.conf
  1. [Service]
  2. Restart=always
  3. RestartSec=30