systemd.generator 中文手册
名称
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
或 systemctlstart 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)