systemd.generator 中文手册

译者:金步国


版权声明

本文译者是一位开源理念的坚定支持者,所以本文虽然不是软件,但是遵照开源的精神发布。

其他作品

本文译者十分愿意与他人分享劳动成果,如果你对我的其他翻译作品或者技术文章有兴趣,可以在如下位置查看现有的作品集:

联系方式

由于译者水平有限,因此不能保证译文内容准确无误。如果你发现了译文中的错误(哪怕是错别字也好),请来信指出,任何提高译文质量的建议我都将虚心接纳。


手册索引 . 指令索引systemd-235

名称

systemd.generator — systemd 单元生成器

大纲

/path/to/generator normal-dir early-dir late-dir

系统生成器目录/run/systemd/system-generators/*
/etc/systemd/system-generators/*
/usr/local/lib/systemd/system-generators/*
/usr/lib/systemd/system-generators/*

用户生成器目录/run/systemd/user-generators/*
/etc/systemd/user-generators/*
/usr/local/lib/systemd/user-generators/*
/usr/lib/systemd/user-generators/*

描述

本文所说的"生成器", 本质上是位于上一小节所列目录中的一些可执行程序。 systemd(1) 将会在其自身刚启动的早期、以及重新加载其自身的配置信息时, 执行这些单元生成器(也就是在加载单元文件之前执行)。 单元生成器能够动态的生成单元文件或者 为了添加依赖关系而创建指向单元文件的符号连接, 从而扩展或者修改现存的定义。主要目的是 动态的将各种配置文件转化为 本地的单元文件。

生成器的加载目录取决于编译时的配置, 一般情况下(--prefix=/usr),就是上一小节所列的目录。 系统单元生成器与用户单元生成器分别从 system-generators/user-generators/ 目录中加载。 位于列表中较前目录中的生成器,将会覆盖列表中较后目录中的同名生成器,也就是列表中较前的目录的优先级也更高。 因此,可以使用高优先级目录中的一个指向 /dev/null 的同名软连接(或空文件), 去屏蔽低优先级目录中的一个同名生成器(也就是禁止它运行)。 注意,生成器目录的优先级顺序与单元目录的优先级顺序并不相同, 具体就是 /run 目录的优先级比 /etc 目录更高。

在安装或更新了生成器之后,必须执行 systemctl daemon-reload 命令才能最终实际生效。 该命令将会删除先前的单元生成器所创建的所有单元文件与软连接, 然后重新运行所有生成器来重新创建单元文件与软连接, 最后再让 systemd 重新加载所有的单元文件。详见 systemctl(1) 手册。

编写单元生成器

需要给单元生成器传递三个目录参数, 以指示所生成的单元文件或软连接的存放位置。 具体如下:

  1. normal-dir

    argv[1] 所指示的目录,用于覆盖存在于 /usr 中但是不存在于 /etc 中的单元文件。 也就是说,位于此目录中的单元文件能够覆盖厂商提供的单元文件, 但是不能覆盖本地用户的单元文件。

  2. early-dir

    argv[2] 所指示的目录,用于覆盖存在于 /usr/etc 中的单元文件。 也就是说,位于此目录中的单元文件能够覆盖所有的单元文件, 包括厂商提供的单元文件以及本地用户的单元文件。

  3. late-dir

    argv[3] 所指示的目录,仅用于扩展现存的单元文件。 也就是说,位于此目录中的单元文件不会覆盖任何现存的单元文件。 现存的所有厂商提供的单元文件以及本地用户的单元文件, 其优先级都比此目录中生成的单元文件的优先级要高。

注意

  • 所有单元生成器都会在同一时间被并行执行。 这就要求所有单元生成器之间不能存在依赖关系, 并且足够小巧以适用这种并发式的运行场景。

  • 单元生成器在系统启动的早期运行,因此它不能依赖于任何其他单元。 单元生成器在运行过程中不能与任何其他进程通信, 包括与日志记录进程 syslog(3)systemd 自身都不行,当然,更不要说使用 systemctl(1) 工具了。 此外,例如 /var/home 这样的非关键文件系统也是尚未挂载的。 单元生成器只能使用最基本的内核功能, 并且只能假定 /sys, /proc, /dev, /usr 已经被挂载。

  • 单元生成器生成的单元文件将会在重新加载配置文件时被删除。 换句话说,就是单元生成器生成的单元的生命周期与 systemd 自身的生命周期是紧紧绑定在一起的。

  • 单元生成器应该仅仅用于生成单元文件, 而不应该做任何与配置相关的工作。 由于上文提到的生命周期的原因, 单元生成器并不适用于为服务单元动态生成配置文件。 如果你需要为某个服务单元动态生成配置文件, 那么你应该使用一个普通的服务单元, 并强迫命令该单元先于被配置的服务单元启动。

  • 由于在系统启动的早期阶段, syslog(3) 处于不可用状态(见上文),必须将日志信息写入到 /dev/kmsg 文件中。

  • 在动态生成的单元文件中使用 SourcePath= 指明其来源于哪个配置文件通常是个好主意。 这样做可以帮助用户轻松的理解该单元是派生于哪个配置文件, 同时也有助于让 systemd 及时的发出警告: 磁盘上的配置文件已经发生变化啦! 但是我现在还没有刷新动态单元。

  • 单元生成器可能会动态生成单元文件, 也可能只是通过软连接将已有的单元添加到其他单元的 .wants/.requires/ 目录中。 通常,在实践中,最佳做法是尽量使用单元生成器从 /usr 中的模版实例化出一个单元, 而不是从头创建一个完整的动态单元文件。 当然,这只能用于仅使用单个参数的场合。

  • 虽然你实际上可以使用 shell 脚本来实现一个单元生成器, 但是我们还是强烈建议你使用C语言来实现。 因为单元生成器都以同步的方式执行, 所以执行速度较慢脚本会拖慢系统的启动速度。

  • 关于单元文件之间的覆盖, 我们应该尽量遵守如下两条规则:

    1. 用户配置应该覆盖厂商的配置。 在大多数情况下,这意味着 /etc 中的内容应该覆盖 /usr 中的内容。

    2. 原生配置应该覆盖非原生配置。 在大多数情况下,这意味着 动态生成的单元文件不应该覆盖系统上已有的单元文件。

    对于上述两个原则,第一个原则应该被严格遵守, 而第二个原则在某些场合有可能会被打破。 因此,在决定使用 argv[1], argv[2], argv[3] 中的哪一个时, 你一般应该默认使用 argv[1]

  • 与其写一堆单元生成器来兼容传统的配置方式, 不如直接舍弃这些不符合 systemd 规范的传统配置方式, 转而使用 systemd 风格的配置方式。 就让那些老旧的配置方式消失在历史的垃圾桶中吧!

例子

例 1. systemd-fstab-generator

systemd-fstab-generator(8) 用于将 /etc/fstab 转换为本地 mount 单元。 该生成器将 argv[1] 用作存放单元文件的目录, 这样,在允许用户明确使用原生单元文件覆盖 /etc/fstab 的同时, 又能确保 /etc/fstab 可以覆盖厂商在 /usr 中默认提供的 mount 单元。

/etc/fstab 被修改之后, 用户应该明确使用 systemctl daemon-reload 命令来重新运行所有生成器,以强制让 systemd 重新加载所有单元文件。要想挂载新添加到 fstab 中的挂载点,可以使用 systemctl start /path/to/mountpointsystemctl start local-fs.target 命令。


例 2. systemd-system-update-generator

systemd-system-update-generator(8) 用于在计划更新系统的时候,将 default.target 软连接临时指向 system-update.target 目标。 因为需要覆盖用户对 default.target 的配置,所以该单元生成器使用 argv[2] 目录存放单元文件。详见 systemd.offline-updates(7) 手册。


例 3. 调试单元生成器

dir=$(mktemp -d)
SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/system-generators/systemd-fstab-generator \
        "$dir" "$dir" "$dir"
find $dir

参见

systemd(1), systemd-cryptsetup-generator(8), systemd-debug-generator(8), systemd-fstab-generator(8), fstab(5), systemd-getty-generator(8), systemd-gpt-auto-generator(8), systemd-hibernate-resume-generator(8), systemd-system-update-generator(8), systemd-sysv-generator(8), systemd.unit(5), systemctl(1), systemd.environment-generator(7)