systemd定时器应用

By Toradex 胡珊逢

1). 简介

定时器任务在 Linux 系统中是一项很普遍的应用,例如定期清理文件,在嵌入式系统则可以定时调用任务采集数据等。Cron 是一个广为人知的定时计划任务管理器,但随着systemd 兴起,systemd timers 也逐渐开始取代传统的 cron。本文就将基于Toradex Linux BSP在Colibri iMX8X 上如使用 systemd 定时器。


Systemd 定时器从触发时间上可以分为两种:

• 单调定时器:从一个特定的时间开始后过一段时间触发,通常可以是系统启动或者systemd 服务单元执行的开始时间

• 实时定时器:在某个特定时间触发,一般指时钟时间,这个就类似于cron定时任务


两种定时满足不同要求的任务,例如需要在某个明确并固定的时间来执行任务,如每周日晚上12点清理日志文件,则可以选择实时定时器。而对于有些无法实现预测执行时间,通常是要在系统启动后才定期执行,如某个任务在启动后每隔10分钟采样传感器数据,由于不确定系统启动时间,所有可以采用单调定时器。或者同时使用两个定时器功能。


Systemd 定时器的配置文件可以分为两部分,定时器单元以 .timer 后缀的systemd单元文件,以及服务单元以 .service 后缀的systemd单元文件。每个 .timer 文件通常对应一个同名的 .service 文件。定时器单元 .timer 文件中的 [Timer] 定义了该定时器何时以及如何触发。该定时器被触发后,执行对应的服务单元 .service,其中的 [Service] 定义了最终被执行的脚本或者应用。下面我们将演示如何使用。



2). 单调定时器

我们将设置一个系统启动 2 分钟后启动并在此之后每隔 15 分钟定期执行的定时任务。下面是所需的定时器单元 test1.timer 和服务单元 test1.service。

./ test1.timer

-----------------------------------------

[Unit]

Description=Run every 15min and on boot

[Timer]

OnBootSec=2min

OnUnitActiveSec=15min

[Install]

WantedBy=timers.target

-----------------------------------------

./ test1.service

-----------------------------------------

[Unit]

Description=Hello World

[Service]

ExecStart=/home/root/HelloWorld

-----------------------------------------

在 .timer 中 OnBootSec= 设置了该定时器需要在系统启动 2 分钟后被执行,OnUnitActiveSec= 设置了该定时器在成功执行后,每隔 15 分钟需要再次被执行。服务单元 test1.service 中 [Service] 定义了 /home/root/HelloWorld 是需要被执行的应用。

把上面两个文件复制到 /etc/system/systemd 目录下,systemd-analyze 命令来核对文件是否有效。

-----------------------------------------

root@colibri-imx8x:/etc/systemd/system# systemd-analyze verify test1*

# 启用 test1 定时任务,并在每次启动后都生效。设置完成后马上重启,查看该任务。

root@colibri-imx8x:/etc/systemd/system# systemctl start test1.timer

root@colibri-imx8x:/etc/systemd/system# systemctl enable test1.timer

root@colibri-imx8x:/etc/systemd/system# sync

root@colibri-imx8x:/etc/systemd/system# reboot

# 命令 systemctl list-timers 可以列出目前激活的定时任务。

root@colibri-imx8x:~# systemctl list-timers

NEXT                         LEFT          LAST PASSED UNIT                         ACTIVATES

Tue 2020-02-11 16:39:19 CST  1min 30s left n/a  n/a    test1.timer                  test1.service

Tue 2020-02-11 16:52:19 CST  14min left    n/a  n/a    systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service

2 timers listed.

Pass --all to see loaded but inactive timers, too.

-----------------------------------------


可以看到在系统启动后,还剩下1分30秒执行test1.service,这个是由 OnBootSec=2min 参数所设置的。

在等待数分钟再次查看定时器任务,test1.service 在14分钟后会被执行,这是由 OnUnitActiveSec=15min 参数所设置。

-----------------------------------------

root@colibri-imx8x:~# systemctl list-timers

NEXT                         LEFT       LAST                         PASSED  UNIT                         ACTIVATES

Tue 2020-02-11 16:52:19 CST  12min left n/a                          n/a     systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service

Tue 2020-02-11 16:54:28 CST  14min left Tue 2020-02-11 16:39:28 CST  26s ago test1.timer                  test1.service

-----------------------------------------

从系统日志里也可以观察到自16:37:25 系统启动后,在 16:39:28 时 HelloWorld 被执行。

-----------------------------------------

root@colibri-imx8x:~# journalctl -u test1.service

-- Logs begin at Tue 2020-02-11 16:37:25 CST, end at Tue 2020-02-11 16:40:49 CST. --

Feb 11 16:39:28 colibri-imx8x systemd[1]: Started Hello World.

Feb 11 16:39:28 colibri-imx8x HelloWorld[3982]: Hello world!

-----------------------------------------

再次等待 15 分钟以后,重新查看日志和定时器任务,在 16:54:30 时HelloWorld 被执行,并且在 13 分钟后会再次执行。这符合定时器任务 test1 的设置预期。

-----------------------------------------

root@colibri-imx8x:~# journalctl -u test1.service

-- Logs begin at Tue 2020-02-11 16:37:25 CST, end at Tue 2020-02-11 16:56:21 CST. --

Feb 11 16:39:28 colibri-imx8x systemd[1]: Started Hello World.

Feb 11 16:39:28 colibri-imx8x HelloWorld[3982]: Hello world!

Feb 11 16:54:30 colibri-imx8x systemd[1]: Started Hello World.

Feb 11 16:54:30 colibri-imx8x HelloWorld[4112]: Hello world!

root@colibri-imx8x:~# systemctl list-timers

NEXT                         LEFT       LAST                         PASSED       UNIT                         ACTIVATES

Tue 2020-02-11 17:09:30 CST  13min left Tue 2020-02-11 16:54:30 CST  1min 31s ago test1.timer                  test1.service

Wed 2020-02-12 16:52:20 CST  23h left   Tue 2020-02-11 16:52:20 CST  3min 42s ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service

-----------------------------------------



3). 实时定时器

这里我们将设置在每个小时第10分钟被执行的实时定时任务。下面是所需的定时器单元 test2.timer 和服务单元 test2.service。

./ test2.timer

-----------------------------------------

[Unit]

Description=Run each 10min of hours

[Timer]

OnCalendar=*-*-* *:10:00

[Install]

WantedBy=timers.target

-----------------------------------------

./ test2.service

-----------------------------------------

[Unit]

Description=Hello Toradex

[Service]

ExecStart=/home/root/HelloToradex

-----------------------------------------

在 test2.timer 中,OnCalendar= 设置定时器任务触发的时间。OnCalendar 的时间格式为 DayOfWeek Year-Month-Day Hour:Minute:Second。

其中 weekday 部分是可以省略的,定时器不匹配星期几,由后面的日期和时间部分来指定。如果填写则必须是英文中星期的缩写或者全称,如星期二是 Web 或者 Wednesday。可以使用 “,”,来指定多个星期天如Mon,Web,Fri。“..”用于指定连续的一段时间,如Mon..Fri 指从周一到周五。“,”和“..”还可以混合使用来指定一周的几天。

日期和时间部分,可以使用 “*” 表示任何符合的日期或者时间。如 2020-*-* 1:23:00 表示2020 年中每天的 1:23:00 时间触发定时器任务。也可以使用 “,” 来指定具体的而时间或日期,2020-1,2-* 1,2:23:00 表示 2020 年中1 月和 2 月每天的1:23:00 和 2:23:00 触发定时器任务。日期、时间还有多种表达方式,包括时区,具体可以查看文章最后的参考链接。

在 .timer 中 OnCalendar=*-*-* *:10:00 将定时器设置为每天每小时第 10 分钟触发。

同样将上述两个文件复制到 /etc/systemd/system 目录,并启动该定时器。

-----------------------------------------

root@colibri-imx8x:/etc/systemd/system# systemctl enable test2.timer

root@colibri-imx8x:/etc/systemd/system# systemctl start test2.timer

# 系统当前时间是 17:29:28,距离18:10:00 还有 40 分钟,因此在 40 分钟后test2 定时器将被触发。

root@colibri-imx8x:~# date

Tue Feb 11 17:29:28 CST 2020

root@colibri-imx8x:~# systemctl list-timers

NEXT                         LEFT       LAST PASSED UNIT                         ACTIVATES

Tue 2020-02-11 17:41:57 CST  12min left n/a  n/a    systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service

Tue 2020-02-11 18:10:00 CST  40min left n/a  n/a    test2.timer                  test2.service

2 timers listed.

Pass --all to see loaded but inactive timers, too.

# 在等待一段时间后,查看日志,18:10:01 的时候 test2 定时器被触发,并执行 /home/root/HelloToradex 程序。

root@colibri-imx8x:~# journalctl -u test2.service

-- Logs begin at Tue 2020-02-11 17:27:02 CST, end at Tue 2020-02-11 18:11:11 CST. --

Feb 11 18:10:01 colibri-imx8x systemd[1]: Started Hello Toradex.

Feb 11 18:10:01 colibri-imx8x HelloToradex[4277]: Hello Toradex!

# 而距下一次触发的时间 19:10:00 还有一个小时。

root@colibri-imx8x:/etc/systemd/system# date

Tue Feb 11 18:10:43 CST 2020

root@colibri-imx8x:~# systemctl list-timers

NEXT                         LEFT       LAST                         PASSED    UNIT                         ACTIVATES

Tue 2020-02-11 19:10:00 CST  59min left Tue 2020-02-11 18:10:01 CST  47s ago   test2.timer                  test2.service

Wed 2020-02-12 17:41:58 CST  23h left   Tue 2020-02-11 17:41:58 CST  28min ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service

2 timers listed.

Pass --all to see loaded but inactive timers, too.

# 那么在一个小时再次查看日志文件,19:10:09 的时候 test2 定时器被触发。

root@colibri-imx8x:~# journalctl -u test2.service

-- Logs begin at Tue 2020-02-11 17:27:02 CST, end at Tue 2020-02-11 19:23:40 CST. --

Feb 11 18:10:01 colibri-imx8x systemd[1]: Started Hello Toradex.

Feb 11 18:10:01 colibri-imx8x HelloToradex[4277]: Hello Toradex!

Feb 11 19:10:09 colibri-imx8x systemd[1]: Started Hello Toradex.

Feb 11 19:10:09 colibri-imx8x HelloToradex[4689]: Hello Toradex!

-----------------------------------------



4). 混合定时器

除了上面单独使用单调或者实时定时器外,我们还可以同时利用两种定时器配置属性。

./ test3.timer

-----------------------------------------

[Unit]

Description=Run each 10min of hours and on boot

[Timer]

OnBootSec=2min

OnCalendar=*-*-* *:10:00

[Install]

WantedBy=timers.target

-----------------------------------------

./ test3.service

-----------------------------------------

[Unit]

Description=Hello Torizon

[Service]

ExecStart=/home/root/HelloTorizon

-----------------------------------------

这里 test3 定时器首先通过 OnBootSec=2min 设置为系统启动后 2 分钟触发,并且由 OnCalendar=*-*-* *:10:00 设置在每个小时第10分钟被执行的实时定时任务。这两个参数分别来自单调定时器和实时定时器配置。

-----------------------------------------

# 启动该定时器后重启系统

root@colibri-imx8x:/etc/systemd/system# systemctl enable test3.timer

root@colibri-imx8x:/etc/systemd/system# sync

root@colibri-imx8x:/etc/systemd/system# reboot

# 系统启动后看到 test3 将会在 1 分 43 秒后触发。

root@colibri-imx8x:~# systemctl list-timers

NEXT                         LEFT          LAST PASSED UNIT                         ACTIVATES

Tue 2020-02-11 19:27:30 CST  1min 42s left n/a  n/a    test3.timer                  test3.service

Tue 2020-02-11 19:40:30 CST  14min left    n/a  n/a    systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service

# 在等待 2 分钟后,test3 已经被触发,并会在42 分钟后再次触发,即 20:10:00。

root@colibri-imx8x:~# systemctl list-timers

NEXT                         LEFT       LAST                         PASSED  UNIT                         ACTIVATES

Tue 2020-02-11 19:40:30 CST  12min left n/a                          n/a     systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service

Tue 2020-02-11 20:10:00 CST  42min left Tue 2020-02-11 19:27:31 CST  19s ago test3.timer                  test3.service

# 从日志中看到,系统在 19:25:35 左右启动,在其后 2 分钟 19:27:31 触发了test3 并执行 /home/root/HelloTorizon。

root@colibri-imx8x:~# journalctl -u test3.service

-- Logs begin at Tue 2020-02-11 19:25:35 CST, end at Tue 2020-02-11 19:28:09 CST. --

Feb 11 19:27:31 colibri-imx8x systemd[1]: Started Hello Torizon.

Feb 11 19:27:31 colibri-imx8x HelloTorizon[4002]: Hello Torizon!

# 在等到 20:10:00 后查看定时器任务,test3 如预期在 20:10:04 的时候被触发,44 分钟后下一次触发。

root@colibri-imx8x:~# systemctl list-timers

NEXT                         LEFT       LAST                         PASSED    UNIT                         ACTIVATES

Tue 2020-02-11 21:10:00 CST  44min left Tue 2020-02-11 20:10:04 CST  15min ago test3.timer                  test3.service

Wed 2020-02-12 19:40:31 CST  23h left   Tue 2020-02-11 19:40:31 CST  44min ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service

# 日志也记录了 20:10:04 时候test3 被触发并执行 /home/root/HelloTorizon。

root@colibri-imx8x:~# journalctl -u test3.service

-- Logs begin at Tue 2020-02-11 19:25:35 CST, end at Tue 2020-02-11 20:25:26 CST. --

Feb 11 19:27:31 colibri-imx8x systemd[1]: Started Hello Torizon.

Feb 11 19:27:31 colibri-imx8x HelloTorizon[4002]: Hello Torizon!

Feb 11 20:10:04 colibri-imx8x systemd[1]: Started Hello Torizon.

Feb 11 20:10:04 colibri-imx8x HelloTorizon[4299]: Hello Torizon!

-----------------------------------------


到这里我们已经演示了不同类型定时器的使用方法,有些读者可能已经发现定时器任务被触发的时间并不是很严格得按照所设定的时间。例如上面 test3 在第二次被触发的时间是 20:10:04,不是 20:10:00。这是因为 Toradex Linux BSP 默认是普通的 Linux 内核,而非实时 Linux,这就导致在任务时间分配上会存在一定的时间抖动。其次,systemd 定时器中的 AccuracySec 参数默认是 1min,这设置了定时器的精度。定时器触发时间窗口为OnCalendar=、OnActiveSec=、OnBootSec=、OnStartupSec=、OnUnitActiveSec= 所设置的时间开始到 AccuracySec= 结束。这样做的目的是减少不必要的 CPU 唤醒,从而降低功耗。为了提高定时器精度,可以将 AccuracySec= 设置到最小的 1us。

为了避免在同一个时间触发多个定时器任务,从而导致系统资源紧张,可以使用 RandomizedDelaySec= 参数,为定时任务添加一个从 0 到 RandomizedDelaySec 的随机延时。



5). Systemd vs. Cron

上面的几个例子已经展示了 systemd 定时器的特点,其于传统的 Cron 比较如下:

优点:

./ Systemd 定时器可以利用 systemd 的依赖关系(如等待网络连接、某个文件的创建、其他应用就位后执行任务)

./ 定时任务会被记录到journald 日志

./ 定时器任务可以在系统/应用启动后触发

./ 可以利用cgroups

./ 可以单独执行任务而不依赖定时器(.timer 和 .service 分开配置)

缺点:

./ 相对复杂的配置过程,Cron 仅需一行命令即可

./ Systemd 定时器默认不支持 MAILTO 功能



6). 总结

定时器任务作为嵌入式系统中常见的应用,systemd 定时器为用户提供更多的可配置功能以及优化选项。本文列举了 systemd 定时器基本操作方法,以及和cron 对比,帮助用户更快得使用。更多的技术细节和功能请参考下面的链接内容。

参考:

https://wiki.archlinux.org/index.php/Systemd/Timershttps://www.freedesktop.org/software/systemd/man/systemd.timer.htmlhttps://jlk.fjfi.cvut.cz/arch/manpages/man/systemd.time.7https://jlk.fjfi.cvut.cz/arch/manpages/man/systemd.timer.5

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容