如何在 Linux 中使用 systemd 计时器安排任务
在 Linux 上安排任务的传统方法是使用 cron 守护进程,指定时间间隔和 要在 crontab 中执行的命令。
Systemd 是目前所有主要 Linux 发行版所采用的相对较新的 init 系统,除其他外,它提供了使用称为计时器的专用单元来调度任务的能力。在本文中,我们将了解它们的结构以及它们的一些用法示例。
在本教程中您将学习:
systemd定时器的基本结构;
如何创建单调且实时的计时器;
如何列出和检查活动计时器;
如何启用定时器;
如何使用瞬态定时器;
基本用法
我们使用服务单元来设置要执行的实际命令(如果您不熟悉基本的 systemd 概念,您可能需要看看我们关于 systemd 服务的文章)。
根据计划的创建方式,计时器可以是:
单调
即时的
单调定时器
Systemd 提供了一系列关键字,我们可以在计时器单元中使用这些关键字来安排任务在预定义事件发生后的一定时间内执行。关键字必须在计时器单元的[Timer]
部分中使用。
让我们看看它们并解释它们的含义:
Keyword | Meaning |
---|---|
OnActiveSec | Schedule the task relatively to the time when the timer unit itself is activated |
OnBootSec | Schedule task relatively to the system boot time |
OnStartupSec | Schedule the task relatively to the time when Systemd started |
OnUnitActiveSec | Schedule the task relatively to the last time the service unit was active |
OnUnitInactiveSec | Schedule the task relatively to the last time the service unit was inactive |
从按键名称可以很容易地猜出,“秒”被用作默认的时间单位。但是,我们可以在值后指定不同的单位(例如 15m – 十五分钟)。正如我们稍后将看到的,关键字可以在计时器单元内组合。
实时计时器
事件也可以用“绝对”术语来安排,类似于我们通过 cron 定义事件的方式,使用另一个 OnCalendar 关键字和允许的时间编码。
这里有些例子:
Time specification | Explanation |
---|---|
Wed 18:00:00 | The task will be executed each Wednesday at 18:00 |
Mon..Wed *-5-27 | The task will be executed the 27th of May of each year, but only on days from Monday to Wednesday |
2020-05-27 | The task will be executed on the 27th of May of the year 2020 at 00:00:00 |
Thu,Fri 2020-*-1,5 11:12:13 | The task will be executed at 11:12:13 of the first and the fifth day of each month of the year 2020, but only if the day is a Thursday or a Friday |
*:0/2 | The task will be executed every two minutes starting from the minute 0 |
15/2 | The task will be executed every two hours starting from 3:00 pm |
hourly | The task will be executed at the beginning of each hour |
daily | The task will be executed each day at 00:00:00 |
weekly | The task will be executed every Monday at 00:00:00 |
monthly | The task will be executed the first day of each month at 00:00:00 |
如果指定的话,工作日必须是英文,可以是缩写形式(星期三)或完整形式(星期三)(大小写无关紧要)。
我们可以使用 ,
字符提供时间值列表,并使用 ..
指定值范围。 *
字符匹配任何值。更多示例可以查阅 systemd.time 联机帮助页。
列出活动计时器
要列出系统中所有活动的计时器单元,我们可以启动 systemctl 的 list-timers 子命令。除非将 --all
选项传递给命令,否则结果中仅包含活动计时器。以下是该命令生成的输出示例:
$ systemctl list-timers
NEXT LEFT LAST PASSED UNIT ACTIVATES
Sun 2020-01-19 19:36:06 CET 5h 15min left Sat 2020-01-18 10:38:59 CET 1 day 3h ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Mon 2020-01-20 00:00:00 CET 9h left Sun 2020-01-19 00:00:16 CET 14h ago man-db.timer man-db.service
Mon 2020-01-20 00:00:00 CET 9h left Sun 2020-01-19 00:00:16 CET 14h ago shadow.timer shadow.service
报告非常详细。它包括 6 列,按顺序描述:
定时器下次运行的时间(NEXT);
下次计时器将再次运行多少次(LEFT);
计时器上次运行时间 (LAST);
自上次计时器运行以来已经过去了多少次 (PASSED);
设置时间表的
计时器单元
(UNIT);由计时器激活的
服务单元
(ACTIVATES)。
一个现实世界的例子
让我们检查一下 man-db.timer
计时器。要检查单元,我们可以使用 systemctl 和 cat 子命令:
$ systemctl cat man-db.timer
这是定时器的定义:
[Unit]
Description=Daily man-db regeneration
Documentation=man:mandb(8)
[Timer]
OnCalendar=daily
AccuracySec=12h
Persistent=true
[Install]
WantedBy=timers.target
我们首先注意到的是 [Unit]
节,它是所有 systemd 单元类型所共有的。这里它用于提供单元的描述:我们可以看到计时器用于执行“每日重新生成 man-db”。
然而,我们最感兴趣的部分是[Timer]
。该节特定于计时器单元:它是定义时间表的地方。 OnCalendar
关键字用于设置每日
实时计划。
我们还可以观察到使用了另外两个关键字:AccuracySec
和 Persistent
。前者用于确定可以启动服务的最大延迟。在本例中,该值为12h
,因此该命令最多可以延迟 12 小时。 AccuracySec
的默认值为 1 分钟
;使用 1ns
表示法(1 纳秒)可获得最佳精度。
另一个关键字Persistent
采用布尔值:如果设置为true,则将计时器上次触发服务的时间保存到磁盘。如果出于某种原因错过了计划的运行,则下次激活计时器单元时,如果在经过的时间内至少会触发一次服务,则会立即启动该服务。例如,这对于在下次机器开机时执行由于系统断电而错过的计划非常有用。
通过仔细查看计时器定义,我们可以注意到要触发的服务没有明确提及:当发生这种情况时,Systemd 会查找与计时器同名的服务单元(因此在本例中 man -db.service
)。要显式引用服务单元,我们必须使用 Unit
关键字。
激活计时器
激活计时器非常简单。我们所要做的就是将它与应该触发的服务一起放置在 /etc/systemd/system
目录中。所有文件就位后,我们运行:
$ sudo systemctl start <timer-unit-name>.timer
要使计时器在启动时(或达到另一个特定目标时)自动激活,我们所要做的就是确保它有一个 [Install]
节,我们在其中指定何时激活发生。
在上面的示例中,WantedBy
关键字用于建立特定目标单元的反向(弱)依赖关系(timers.target
- 在启动过程中很早就达到的目标) )在我们正在配置的计时器单元上:在达到该目标之前,我们的单元应该被激活。
瞬态定时器
可以“即时”安排任务的执行,而无需使用 systemd-run 手动创建专用计时器和服务单元。如果全局运行,该命令会在 /run/systemd/transient
目录内以及 /run/user/
目录(如果以特定用户身份启动)(--user
选项)。
让我们看一个例子。假设我们希望每分钟将日期和时间记录到文件中。我们会运行:
$ systemd-run --user --on-calendar '*:0/1' /bin/sh -c "date >> ~/log.txt"
Running timer as unit: run-r81a4fef38154401bbd8cdbd1e5c19d04.timer
Will run service as unit: run-r81a4fef38154401bbd8cdbd1e5c19d04.service
从命令的输出中我们可以看到,已经创建了两个临时单元:run-r81a4fef38154401bbd8cdbd1e5c19d04.timer
和run-r81a4fef38154401bbd8cdbd1e5c19d04.service
。
如果我们检查日志文件,我们可以看到计时器工作正常:
$ cat ~/log.txt
Mon 20 Jan 2020 11:20:54 AM CET
Mon 20 Jan 2020 11:21:54 AM CET
Mon 20 Jan 2020 11:22:54 AM CET
Mon 20 Jan 2020 11:23:54 AM CET
Mon 20 Jan 2020 11:24:54 AM CET
Mon 20 Jan 2020 11:25:54 AM CET
Mon 20 Jan 2020 11:26:54 AM CET
要删除/禁用瞬态计时器,我们所要做的就是停止它。在这种情况下,我们将运行:
$ systemctl --user stop run-r81a4fef38154401bbd8cdbd1e5c19d04.timer
结论
在本教程中,我们学习了如何使用 systemd 计时器作为 cronjobs 的替代方案来安排系统任务。我们了解了计时器背后的基本结构,如何通过专用关键字(例如 OnBootSec
或 OnCalendar
)定义单调和实时计划,如何列出和检查活动计时器,如何启用和禁用它们。
最后,我们了解了如何使用瞬态计时器。在本文中,您应该找到开始使用计时器所需的一切。不过,有关更详细的信息,您可能需要查看官方文档,可以在线查看,也可以查阅 systemd.timer
联机帮助页。