本文译者是一位开源理念的坚定支持者,所以本文虽然不是软件,但是遵照开源的精神发布。
本文译者十分愿意与他人分享劳动成果,如果你对我的其他翻译作品或者技术文章有兴趣,可以在如下位置查看现有的作品集:
由于译者水平有限,因此不能保证译文内容准确无误。如果你发现了译文中的错误(哪怕是错别字也好),请来信指出,任何提高译文质量的建议我都将虚心接纳。
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) 将会在其自身刚启动的早期、以及重新加载其自身的配置信息时, 执行这些单元生成器(也就是在加载单元文件之前执行)。 生成器的主要目的是将非单元配置转化为动态生成的单元文件。
每个生成器都接受三个目录参数,用作输出目录。
生成器在这三个目录中动态生成单元文件(常规单元、
实例单元、模板单元)、
单元配置片段(位于 .d/
目录中)、
指向单元文件的软连接(表示单元间额外的依赖关系、表示单元的别名、表示基于现有模板实例化的单元)。
因为这些目录包含在
systemd(1)
的单元加载路径中,
所以生成的配置能够扩展或覆盖现有的定义。
不同的生成器输出目录拥有不同的优先级,
由高到低排列,依次为:
…/generator.early
、
/etc
、
…/generator
、
/usr
、
…/generator.late
。
详见下文以及
systemd.unit(5) 手册。
生成器的加载目录取决于编译时的配置,
一般情况下(--prefix=/usr),就是上一小节所列的目录。
系统单元生成器与用户单元生成器分别从
system-generators/
与
user-generators/
目录中加载。
位于列表中较前目录中的生成器,将会覆盖列表中较后目录中的同名生成器,也就是列表中较前的目录的优先级也更高。
因此,可以使用高优先级目录中的一个指向
/dev/null
的同名软连接(或空文件),
去屏蔽低优先级目录中的一个同名生成器(也就是禁止它运行)。
注意,生成器目录的优先级顺序与单元目录的优先级顺序并不相同,
具体就是
/run
目录的优先级比
/etc
目录更高。
在安装或更新了生成器之后, 必须执行 systemctl daemon-reload 命令才能最终实际生效。 该命令将会删除先前的单元生成器所创建的所有单元文件与软连接, 然后重新运行所有生成器来重新创建单元文件与软连接,最后再让 systemd 重新加载所有的单元文件。详见 systemctl(1) 手册。
需要给单元生成器传递三个目录参数, 以指示所生成的单元文件或软连接的存放位置。 默认情况下,这些参数是包含在 systemd 单元目录中的运行时目录, 但是出于调试目的,也可以设为其他路径。
normal-dir
对于系统生成器来说,一般是 /run/systemd/generator
目录;对于用户生成器来说,一般是
$XDG_RUNTIME_DIR/generator
目录。
此目录中的单元文件能够覆盖厂商提供的单元文件(/usr
),
但是不能覆盖本地系统管理员/普通用户的单元文件(/etc
)。
early-dir
对于系统生成器来说,一般是 /run/systemd/generator.early
目录;对于用户生成器来说,一般是
$XDG_RUNTIME_DIR/generator.early
目录。
此目录中的单元文件能够覆盖
/usr
, /run
,
/etc
中的单元文件。
也就是说,此目录中的单元文件能够覆盖所有单元文件,
包括厂商提供的单元文件以及本地系统管理员/普通用户的单元文件。
late-dir
对于系统生成器来说,一般是 /run/systemd/generator.late
目录;对于用户生成器来说,一般是
$XDG_RUNTIME_DIR/generator.late
目录。
此目录中的单元文件不会覆盖任何现存的单元文件,
仅用于扩展单元文件。
任何现存的单元文件的优先级都高于此目录中的单元文件。
所有单元生成器都会在同一时间被并行执行。 这就要求所有单元生成器之间不能存在依赖关系, 并且足够小巧以适用这种并发式的运行场景。
单元生成器在系统启动的早期运行,因此它不能依赖于任何其他单元。
单元生成器在运行过程中不能与任何其他进程通信,
包括与日志记录进程
syslog(3)
或 systemd 自身都不行,当然,更不要说使用
systemctl(1)
工具了。此外,例如 /var
与
/home
这样的非关键文件系统也是尚未挂载的。
单元生成器只能使用最基本的内核功能,
并且只能假定 /sys
, /proc
,
/dev
, /usr
已经被挂载。
单元生成器生成的单元文件将会在重新加载配置文件时被删除。 换句话说,就是单元生成器生成的单元的生命周期与 systemd 自身的生命周期是紧紧绑定在一起的。
单元生成器应该仅仅用于生成单元文件与指向单元文件的软连接, 而不应该做任何与配置相关的工作。 由于上文提到的生命周期的原因,单元生成器并不适用于为服务单元动态生成配置文件。 如果你需要为某个服务单元动态生成配置文件, 那么你应该使用一个普通的服务单元, 并强迫命令该单元先于被配置的服务单元启动。
由于在系统启动的早期阶段,
syslog(3)
处于不可用状态(见上文),必须将日志信息写入到
/dev/kmsg
文件中。
在动态生成的单元文件中使用 SourcePath=
指明其来源于哪个配置文件通常是个好主意。
这样做可以帮助用户轻松的理解该单元是派生于哪个配置文件,
同时也有助于让 systemd 及时的发出警告:
磁盘上的配置文件已经发生变化,
但是我现在还没有刷新动态单元。
单元生成器可能会动态生成单元文件,
也可能只是通过软连接将已有的单元添加到其他单元的 .wants/
或
.requires/
目录中。
通常,在实践中,最佳做法是尽量使用单元生成器从 /usr
中的模版实例化出一个单元,
而不是从头创建一个完整的动态单元文件。
当然,这只能用于仅使用单个参数的场合。
虽然你实际上可以使用 shell 脚本来实现一个单元生成器,但是我们还是强烈建议你使用C语言来实现。 因为单元生成器都以同步的方式执行, 所以执行速度较慢脚本会拖慢系统的启动速度。
关于单元文件之间的覆盖, 我们应该尽量遵守如下两条规则:
用户配置应该覆盖厂商的配置。
在大多数情况下,这意味着 /etc
中的内容应该覆盖
/usr
中的内容。
原生配置应该覆盖非原生配置。 在大多数情况下,这意味着 动态生成的单元文件不应该覆盖系统上已有的单元文件。
对于上述两个原则,第一个原则应该被严格遵守,而第二个原则在某些场合有可能会被打破。 因此,在决定使用 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/mountpoint
或 systemctl
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-rc-local-generator(8), systemd-system-update-generator(8), systemd-sysv-generator(8), systemd.unit(5), systemctl(1), systemd.environment-generator(7)