systemd/Timers
定时器(Timers)是名称以 .timer 结尾的 systemd 单元文件,用于控制 .service 文件或事件。定时器可以作为 cron 的替代方案(详见 #作为 cron 的替代方案)。定时器内置支持日历时间事件、单调时间事件,并且可以异步运行。
定时器单元
定时器是带有 .timer 后缀的 systemd 单元文件。定时器与其他 单元配置文件 类似,从相同的路径加载,但包含一个 [Timer] 部分,用于定义定时器何时以及如何激活。定时器被定义为以下两种类型之一:
- 实时定时器(又称挂钟定时器)在日历事件上激活,方式与 cron 任务相同。使用
OnCalendar=选项来定义它们。 - 单调定时器在相对于变化的起始点的一段时间后激活。如果计算机临时休眠或关机,它们会停止。有多种不同的单调定时器,但其形式均为:
OnTypeSec=。常见的单调定时器包括OnBootSec和OnUnitActiveSec。
有关定时器选项的完整说明,请参阅 systemd.timer(5)。日历事件和时间段的参数语法在 systemd.time(7) 中定义。
timers.target 目标,它会设置所有在启动后应处于激活状态的定时器(详见 systemd.special(7))。要使用它,请将 WantedBy=timers.target 添加到定时器的 [Install] 部分,并 启用 (enable) 该定时器单元。服务单元
对于每个 .timer 文件,通常存在一个匹配的 .service 文件(例如 foo.timer 和 foo.service)。.timer 文件激活并控制 .service 文件。.service 文件不需要 [Install] 部分,因为被启用的是 定时器 单元。如有必要,可以使用定时器 [Timer] 部分中的 Unit= 选项来控制名称不同的单元。
管理
要使用 定时器 单元,请像管理其他单元一样 启用 并 启动 它(记得加上 .timer 后缀)。要查看所有已启动的定时器,请运行:
$ systemctl list-timers
NEXT LEFT LAST PASSED UNIT ACTIVATES Thu 2014-07-10 19:37:03 CEST 11h left Wed 2014-07-09 19:37:03 CEST 12h ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service Fri 2014-07-11 00:00:00 CEST 15h left Thu 2014-07-10 00:00:13 CEST 8h ago logrotate.timer logrotate.service
- 要列出所有定时器(包括未激活的),请使用
systemctl list-timers --all。 - 由定时器启动的服务状态很可能是 inactive (未激活),除非它当前正被触发运行。
- 如果定时器变得不同步,删除
/var/lib/systemd/timers中的stamp-*文件(对于用户定时器则是~/.local/share/systemd/)可能会有帮助。这些是零长度文件,记录了每个定时器上次运行的时间。如果删除,它们将在定时器下次启动时重新创建。
示例
服务单元文件可以开箱即用地配置定时器调度。以下示例调度 foo.service 通过对应的 foo.timer 定时器运行。
单调定时器
一个在开机 15 分钟后启动,并在系统运行期间每周再次启动一次的定时器。
/etc/systemd/system/foo.timer
[Unit] Description=Run foo weekly and on boot [Timer] OnBootSec=15min OnUnitActiveSec=1w [Install] WantedBy=timers.target
实时定时器
一个每周启动一次(周一凌晨 12:00)的定时器。激活时,如果错过了上次启动时间(选项 Persistent=true),例如由于系统关机,它会立即触发服务:
/etc/systemd/system/foo.timer
[Unit] Description=Run foo weekly [Timer] OnCalendar=weekly Persistent=true [Install] WantedBy=timers.target
当需要更具体的日期和时间时,OnCalendar 事件使用以下格式:
DayOfWeek Year-Month-Day Hour:Minute:Second
星号可用于指定任何值,逗号可用于列出可能的值。由 .. 分隔的两个值表示连续范围。
在下面的示例中,服务在每个月的前四天中午 12:00 运行,但仅限当天是周一或周二的情况。
OnCalendar=Mon,Tue *-*-01..04 12:00:00
要在每个月的第一个周六运行服务,请使用:
OnCalendar=Sat *-*-1..7 18:00:00
使用 DayOfWeek 部分时,必须至少指定一个星期几。如果你想让某项任务每天凌晨 4 点运行,请使用:
OnCalendar=*-*-* 4:00:00
要在不同时间运行服务,可以多次指定 OnCalendar。在下面的示例中,服务在工作日的 22:30 和周末的 20:00 运行。
OnCalendar=Mon..Fri 22:30 OnCalendar=Sat,Sun 20:00
你还可以在指令末尾指定时区(使用 timedatectl list-timezones 列出接受的值):
OnCalendar=*-*-* 02:00:00 Europe/Paris
更多信息可在 systemd.time(7) 中找到。
OnCalendar时间规范可以使用 systemd-analyze 工具的calendar选项进行测试,以验证其有效性并计算在定时器单元文件中使用时下一次触发的时间。例如,可以使用systemd-analyze calendar weekly或systemd-analyze calendar "Mon,Tue *-*-01..04 12:00:00"。添加--iterations=N可以要求打印更多次的迭代结果。faketime命令对于测试上述命令的各种场景特别有用;它包含在 libfaketime 软件包中。- 像
daily和weekly这样的特殊事件表达式是指特定的开始时间,因此任何共享此类日历事件的定时器都将同时启动。如果定时器的服务竞争系统资源,共享启动事件可能会导致系统性能下降。[Timer]部分中的RandomizedDelaySec选项通过随机错开每个定时器的启动时间来避免此问题。参见 systemd.timer(5)。 - 在
[Timer]部分添加选项AccuracySec=1us,以避免AccuracySec默认值 1m 带来的不准确性。另见 systemd.timer(5)。 - 某些选项(如
WakeSystem)可能需要特定的系统权限,并可能阻止定时器启动,导致以下错误消息:“Failed to enter waiting state: Operation not supported”和“Failed with result 'resources'.”。
瞬时定时器单元
可以使用 systemd-run 创建瞬时 .timer 单元。也就是说,可以在没有服务文件的情况下设置一个命令在指定时间运行。例如,以下命令在 30 秒后 touch 一个文件:
# systemd-run --on-active=30 /bin/touch /tmp/foo
也可以指定一个没有定时器文件的现有服务文件。例如,以下命令在 12.5 小时后启动名为 someunit.service 的 systemd 单元:
# systemd-run --on-active="12h 30m" --unit someunit.service
更多信息和示例请参见 systemd-run(1)。
作为 cron 的替代方案
尽管 cron 无疑是最著名的作业调度器,但 systemd 定时器可以作为一个替代方案。
优势
使用定时器的主要好处来自于每个作业都有其对应的 systemd 服务。其中的一些好处包括:
- 作业可以轻松地独立于其定时器启动。这简化了调试过程。
- 每个作业都可以配置在特定环境中运行(见 systemd.exec(5))。
- 作业可以附加到 cgroups。
- 作业可以设置为依赖于其他 systemd 单元。
- 作业记录在 systemd 日志中,便于调试。
注意事项
有些用 cron 很容易实现的事情,仅靠定时器单元可能较难实现:
- 创建:要使用 systemd 设置定时作业,你需要创建两个文件并运行
systemctl命令,而 crontab 只需要添加一行。 - 邮件:没有内置的等效于 cron 的
MAILTO功能用于在作业失败时发送电子邮件。参见 systemd#通过电子邮件通知 了解使用OnFailure=设置类似功能的示例。
另请注意,默认情况下,用户 定时器单元仅在活跃的用户登录会话期间运行。但是,驻留 (lingering) 可以使服务在用户没有活跃登录会话的情况下也能在开机时运行。
使用 crontab
可以通过安装解析传统 crontab 以配置定时器的软件包来解决一些注意事项。systemd-cron-next-gitAUR 和 systemd-cronAUR 是两个此类软件包。它们可以提供缺失的 MAILTO 功能。
此外,与 crontabs 类似,可以使用 systemctl 获取所有调度作业的统一视图。参见 #管理。
手动设置
除了从现有 crontab 迁移之外,有时也希望能使用与 cron 相同的周期。为了避免为每个要定期启动的服务创建定时器的繁琐任务,可以使用 模板单元,例如:
/etc/systemd/system/monthly@.timer
[Unit] Description=Monthly Timer for %i service [Timer] OnCalendar=*-*-1 02:00:00 AccuracySec=6h RandomizedDelaySec=1h Persistent=true Unit=%i.service [Install] WantedBy=default.target
然后只需要 启用/启动 monthly@unit_name.timer。
monthly@btrfs-scrub@mnt-$(systemd-escape bbb76c63-e4ac-4e39-8897-a120c5d30686).timer。技巧与提示
处理“生存时间”
某些软件会跟踪自上次运行以来经过的时间,例如,如果上次下载在不到 24 小时前结束,则阻止更新数据库。
默认情况下,定时器不会跟踪它们启动的任务何时结束。为了解决这个问题,我们可以使用 OnUnitInactiveSeconds:
/etc/systemd/system/daily-inactive@.timer
[Unit] Description=Launch %i service 24hours after it deactivated [Timer] OnUnitInactiveSec=1day1sec Unit=%i.service Persistent=true [Install] WantedBy=default.target
桌面通知
systemd-timer-notifyAUR 提供自动桌面通知,帮助你注意到 systemd 服务何时由定时器触发并正在运行。当服务完成时,通知将自动关闭。
这对于了解 CPU 占用率为何很高,或防止在备份服务未完成时关机非常有用。
有关更多详细信息和配置选项,请访问 https://gitlab.com/Zesko/systemd-timer-notify
参见
- systemd.timer(5)
- Fedora:Features/SystemdCalendarTimers
- Gentoo:Systemd#Timer services
- systemd-cron — 提供运行 cron 脚本的 systemd 单元;使用 systemd-crontab-generator 转换 crontabs
- systemd-cron-next — 从 crontab 和 anacrontab 文件生成定时器/服务的工具