systemd (简体中文)/Timers (简体中文) - ArchWiki

Jump to content
< Systemd (简体中文)

翻译状态：本文是 Systemd/Timers 的翻译。上次翻译日期：2022-03-25。如果英文版本有所更改，则您可以帮助同步翻译。

相关文章

- systemd (简体中文)
- Systemd (简体中文)/User (简体中文)
- systemd FAQ (简体中文)
- cron (简体中文)

Timers 是以 `.timer` 为后缀名的 systemd 单元文件，用于控制 `.service` 文件或事件。Timers 可用来替换 cron（阅读 #替代 cron）。Timers 内置了实时定时事件和单调定时事件的支持，并可以异步执行这些事件。

## 定时器单元

Timers 是以 `.timer` 为后缀的 systemd 单元文件。Timers 和其他单元配置文件是类似的，它通过相同的路径加载，不同的是包含了 `[Timer]` 部分。 `[Timer]` 部分定义了何时以及如何激活定时事件。Timers 可以被定义成以下两种类型：

- 实时定时器（亦称“挂钟定时器”）通过日历事件激活（类似于 cronjobs）定时任务。使用 `OnCalendar=` 来定义实时定时器。
- 单调定时器即在一个时间点经过一段时间后激活定时任务。所有的单调计时器都遵循如下形式： `OnTypeSec=`。 `OnBootSec` 和 `OnActiveSec` 是常用的单调定时器。

要查阅完整的定时器选项，参见 systemd.timer(5)。关于日历事件和时间段的定义参见 systemd.time(7)。

## 服务单元

每个 `.timer` 文件所在目录都得有一个对应的 `.service` 文件（如 `foo.timer` 和 `foo.service`）。`.timer` 文件激活并控制 `.service` 文件。对应的 `.service` 文件中不需要包含 `[Install]` 部分，因为这由 timer 单元接管。必要时可以通过在定时器的 `[Timer]` 部分指定 `Unit=` 选项来控制一个与定时器不同名的服务单元。

## 管理

使用 timer 单元只需像其他单元一样 enable 或 start 即可（别忘了添加 `.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` 下的 `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

### 实时定时器

定义一个每周执行一次（具体来讲，指周一凌晨零点）的定时器。如果上次未执行（比如说系统当时没有开机，这个行为由 `Persistent=true` 定义）就立即执行服务。

    /etc/systemd/system/foo.timer

    [Unit]
    Description=Run foo weekly

    [Timer]
    OnCalendar=weekly
    Persistent=true

    [Install]
    WantedBy=timers.target

更精确的时间可以通过 `OnCalendar` 参数以下列方式指定：

    星期 年-月-日 时:分:秒

可以用星号来表示任意值，用逗号分列可能值。以 `..` 分隔的两个值指两个值间的连续序列。

这是在每个月的前四天晚上中午 12 点，但仅当这一天是周一和周四时运行的例子：

    OnCalendar=Mon,Tue *-*-01..04 12:00:00

在每个月的第一个星期六运行：

     OnCalendar=Sat *-*-1..7 18:00:00

如果不需要以星期几来界定，比如说在每天四点运行，请去掉星期项：

     OnCalendar=*-*-* 4:00:00

在不同的时间运行一个服务可以以多次指定 `OnCalendar` 的方式表示。在下面的例子中，这个服务在周内的 22:30 和周末的 20:00 运行：

     OnCalendar=Mon..Fri 22:30
     OnCalendar=Sat,Sun 20:00

更多信息请参阅 systemd.time(7)。

提示：

- 可以用 systemd-analyze 工具检测 `OnCalendar` 项是否填写正确，并计算下一次这个条件成立的时间。比如说可以用 `systemd-analyze calendar weekly` 或 `systemd-analyze calendar "Mon,Tue *-*-01..04 12:00:00"`。
- libfaketime 包提供的 `faketime` 命令在测试各种情况时非常有用。
- 特殊的事件表达式如 `daily` 和 `weekly` 表示 特定的启动时间，因此任何共享该日历事件的定时器将同时启动。如果该定时器的服务会竞争系统资源，那么共享该日历事件的定时器可能会引起系统性能下降。使用 `[Timer]` 部分中的 `RandomizedDelaySec` 选项可以通过随机推迟定时器启动来避免这个问题。参见 systemd.timer(5)。
- 向 `[Timer]` 部分添加 `AccuracySec=1us` 选项可以避免原有默认的精确值 1 分钟造成的误差。参见 systemd.timer(5)。

## 瞬态 .timer 单元

可以使用 `systemd-run` 创建一个瞬态 `.timer` 单元。即可以在不创建服务文件的情况下设置定时运行某个命令。比如说下面的命令可以在 30 秒钟后创建一个文件：

    # systemd-run --on-active=30 /bin/touch /tmp/foo

也可以建立指定一个已经存在但没有定义 timer 的服务文件。比如说，下面的命令会在 12 小时 30 分钟后启动名为 `someunit.service` 的服务：

    # systemd-run --on-active="12h 30m" --unit someunit.service

更多信息和例子参见 systemd-run(1)。

## 替代 cron

尽管 cron 毋庸置疑是最有名的计划任务管理器，systemd 定时器仍可以作为一个替代品。

### 优势

使用定时器的最主要的优势在于每个任务都有它自己的 systemd 服务。这样做的好处包括：

- 任务可以简单地独立于他们的定时器启动，简化调试。
- 每个任务可配置运行于特定的环境中（参见 systemd.exec(5)）。
- 任务可以使用 cgroups (简体中文) 特性。
- 任务可以配置依赖于其他 systemd 单元。
- 任务会被记录于 systemd 日志，便于调试。

### 注意事项

有些可以用 cron 轻易做到的事情仅仅使用 timer 组件会比较难办：

- 创建过程：相比于在 crontab 中只需添加一行任务，使用 systemd 配置计划任务需要创建两个文件并运行好几次 `systemctl` 命令。
- 邮件：目前还没有内置与 cron `MAILTO` 类似的任务失败时发送邮件的功能。可以在每个服务文件中配置 `OnFailure=` 来实现同样的功能。

另外， 用户定时器默认只会在用户登录时进行。但是，lingering 允许在启动，且用户没有活动登录会话时激活。

- 随机延时：目前没还有内置与 cron 类似的 `RANDOM_DELAY` 功能来指定一个数字用于定时器延时执行。（参见 bug report）。 你不想同时执行的服务必须手动设置它们的定时器。

  注意： `AccuracySec` 选项对于随机错开定时器执行时间是 没有 作用的，因为它"会在所有本地定时器单元间同步" (`systemd.timer(5)`)。换句话说， `AccuracySec` 会以相同的量改变所有定时器激活时间。例如，所有 `OnCalendar=daily` 的定时器单元，指定 `AccuracySec=15m` 将同时在 00:00 到 00:15 触发相关的服务。

### 发送邮件

可以配置 systemd 在单元失败时发送电子邮件。Cron 的 `MAILTO` 会在任务向标准输出或标准错误输出时发送邮件，许多任务只在发生错误时输出。首先需要两个文件：一个用来发送邮件的可执行文件和一个用于启动这个可执行文件的 .service 文件。在本例中，可执行文件只是一个由提供 `smtp-forwarder` 的包中给出的调用 `sendmail` 的shell 脚本：

    /usr/local/bin/systemd-email

    #!/bin/sh

    /usr/bin/sendmail -t <<ERRMAIL
    To: $1
    From: systemd <root@$HOSTNAME>
    Subject: $2
    Content-Transfer-Encoding: 8bit
    Content-Type: text/plain; charset=UTF-8

    $(systemctl status --full "$2")
    ERRMAIL

不论你用什么可执行文件，应该都会像这个脚本一样接收至少两个参数：目标邮箱地址和用来获取状态的单元文件名。我们创建的 .service 文件会传递这些参数：

    /etc/systemd/system/status_email_user@.service

    [Unit]
    Description=status email for %i to user

    [Service]
    Type=oneshot
    ExecStart=/usr/local/bin/systemd-email address %i
    User=nobody
    Group=systemd-journal

其中，`user` 是被通知的用户名，`address` 是该用户的邮件地址。尽管这个地址被硬编码在代码里，但是报告的单元文件会在实例参数中指定，因此可以作为许多其他单元的邮件服务端。此时你可以 start `status_email_user@dbus.service` 服务来检查你能否收信。

成功后，仅需 编辑 你需要发送失败邮件的服务，向 `[Unit]` 部分添加 `OnFailure=status_email_user@%n.service`。`%n` 会向模板传递单元的名字。

注意：

- 如果你根据 sSMTP#Security 配置了 sSMTP 安全，`nobody` 用户会没有访问 `/etc/ssmtp/ssmtp.conf` 文件的权限，`systemctl start status_email_user@dbus.service` 命令就会失败。一个解决办法是使用 `root` 作为 `status_email_user@.service` 单元的执行者。
- 如果你希望在你的邮件脚本中使用 `mail -s somelogs address`，`mail` 会以 fork 的形式启动，systemd 会把在你的脚本退出时一并将这个进程结束掉。可以用 `mail -Ssendwait -s somelogs address` 来让 mail 不以 fork 的形式启动。

### 使用 crontab

有些注意事项中的部分可以通过安装一些将传统 crontab 项目翻译成 timers 配置的包来完成，例如AUR 中的 systemd-crontab-generatorAUR[损坏的链接：package not found] 和 systemd-cronAUR 包。他们也可以提供缺失的 `MAILTO` 特性。

同样，与 crontab 一样，可以通过 `systemctl` 来获取一个统一的计划任务视图。参见 #管理章节。

## 参见

- systemd.timer(5)
- Fedora Project wiki page systemd 的实时定时器
- Gentoo wiki section on systemd timer services
- systemd-cron-next — 一个从 crontab 和 anacrontab 文件生成 timers/services 文件的工具

  https://github.com/systemd-cron/systemd-cron-next || systemd-cron-nextAUR

- systemd-cron — 提供 systemd 单元运行 cron 脚本；使用 systemd-crontab-generator 转换 crontabs

  https://github.com/systemd-cron/systemd-cron || systemd-cronAUR

- Systemd Timers for Scheduling Tasks

Category:

- System administration (简体中文)

Hidden category:

- Pages with broken package links