systemd/定时器
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=
來實現同樣的功能。
另外, systemd用戶服務定時器默認只會在用戶登錄時進行。但是,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 文件的工具
- systemd-cron — 提供 systemd 單元運行 cron 腳本;使用 systemd-crontab-generator 轉換 crontabs