systemd/定时器
定时器是 systemd 单元文件,其名称以 .timer 结尾,用于控制 .service 文件或事件。定时器可以作为 cron 的替代方案(阅读 #作为 cron 的替代品)。定时器内置了对日历时间事件、单调时间事件的支持,并且可以异步运行。
定时器单元
定时器是 systemd 单元文件,后缀为 .timer。定时器与其他 单元配置文件 类似,从相同的路径加载,但包含一个 [Timer]
部分,用于定义定时器何时以及如何激活。定时器被定义为两种类型之一
- 实时定时器(又名挂钟定时器)在日历事件时激活,方式与 cronjobs 相同。选项
OnCalendar=
用于定义它们。 - 单调定时器在相对于可变起点的时 span 后激活。如果计算机暂时挂起或关闭,它们将停止。有许多不同的单调定时器,但都具有以下形式:
OnTypeSec=
。常见的单调定时器包括OnBootSec
和OnUnitActiveSec
。
有关定时器选项的完整说明,请参阅 systemd.timer(5)。日历事件和时间 span 的参数语法在 systemd.time(7) 中定义。
timers.target
,它设置所有应在启动后激活的定时器(有关详细信息,请参见 systemd.special(7))。要使用它,请将 WantedBy=timers.target
添加到定时器的 [Install]
部分,并启用定时器单元。服务单元
对于每个 .timer 文件,都存在一个匹配的 .service 文件(例如 foo.timer
和 foo.service
)。.timer 文件激活并控制 .service 文件。.service 不需要 [Install]
部分,因为启用的是 timer 单元。如有必要,可以使用定时器的 [Timer]
部分中的 Unit=
选项来控制不同名称的单元。
管理
要使用 timer 单元,像其他单元一样启用和启动它(记住添加 .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
。 - 由定时器启动的服务的状态很可能处于非活动状态,除非它当前正在被触发。
- 如果定时器失去同步,删除其
/var/lib/systemd/timers
(或用户定时器情况下的~/.local/share/systemd/
)中的stamp-*
文件可能会有所帮助。这些是零长度文件,用于标记每个定时器上次运行的时间。如果删除,它们将在下次启动定时器时重建。
示例
服务单元文件可以使用定时器开箱即用地进行调度。以下示例计划运行 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)。 - 将选项
AccuracySec=1us
添加到[Timer]
部分,以避免AccuracySec
的 1m 默认值不准确。另请参见 systemd.timer(5)。 - 某些选项 (
WakeSystem
) 可能需要特定的系统功能,并阻止定时器启动,从而导致以下错误消息:“Failed to enter waiting state: Operation not supported” 和 “Failed with result 'resources'.”。
瞬态定时器单元
可以使用 systemd-run
创建瞬态 .timer 单元。也就是说,可以设置一个命令在指定时间运行,而无需服务文件。例如,以下命令在 30 秒后触摸一个文件
# 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=
设置类似功能的示例。
另请注意,默认情况下,用户定时器单元仅在活动用户登录会话期间运行。但是,持久化可以使服务在启动时运行,即使用户没有活动的登录会话。
使用 crontab
通过安装解析传统 crontab 以配置定时器的软件包,可以解决几个注意事项。systemd-cron-next-gitAUR 和 systemd-cronAUR 是两个这样的软件包。这些可以提供缺少的 MAILTO
功能。
此外,与 crontab 一样,可以使用 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
桌面通知
The systemd-timer-notifyAUR 提供自动桌面通知,帮助您注意到 systemd 服务何时由定时器触发并正在运行。通知将在服务完成后自动关闭。
这有助于理解为什么 CPU 使用率很高,或者在备份服务尚未完成时防止关机。
有关更多详细信息和配置选项,请访问 https://gitlab.com/Zesko/systemd-timer-notify
参见
- systemd.timer(5)
- Fedora:Features/SystemdCalendarTimers
- Gentoo:Systemd#Timer services
- systemd-cron — 提供 systemd 单元来运行 cron 脚本;使用 systemd-crontab-generator 转换 crontab
- systemd-cron-next — 从 crontab 和 anacrontab 文件生成定时器/服务的工具