systemd/定时器
定时器是 systemd 单元文件,其名称以 .timer 结尾,用于控制 .service 文件或事件。定时器可以作为 cron 的替代品使用(请阅读 #作为 cron 的替代品)。定时器内置支持日历时间事件、单调时间事件,并且可以异步运行。
定时器单元
定时器是 systemd 单元文件,后缀为 .timer。定时器与其他 单元配置文件 类似,从相同的路径加载,但包含一个 [Timer]
区块,用于定义定时器何时以及如何激活。定时器被定义为两种类型之一
- 实时定时器(又称挂钟定时器)在日历事件时激活,方式与 cronjobs 相同。
OnCalendar=
选项用于定义它们。 - 单调定时器在相对于变化的起始点的时间跨度后激活。如果计算机暂时挂起或关机,它们将停止。有许多不同的单调定时器,但都具有以下形式:
OnTypeSec=
。常见的单调定时器包括OnBootSec
和OnUnitActiveSec
。
有关定时器选项的完整说明,请参阅 systemd.timer(5)。日历事件和时间跨度的参数语法在 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
等效项来在作业失败时发送电子邮件。有关使用OnFailure=
设置类似功能的示例,请参阅 systemd#使用电子邮件通知。
另请注意,默认情况下,用户定时器单元仅在活动用户登录会话期间运行。但是,持久化可以使服务即使在用户没有活动登录会话时也能在启动时运行。
使用 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
RandomizedDelaySec
而不仅仅是 AccuracySec
以避免定时器启动的所有单元一次性触发的重要性,请参阅 systemd.timer(5) § OPTIONS。然后,只需启用/启动 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 — 提供 systemd 单元来运行 cron 脚本;使用 systemd-crontab-generator 转换 crontabs
- systemd-cron-next — 用于从 crontab 和 anacrontab 文件生成定时器/服务的工具