Systemd 作為 Linux 的系統啟動器,功能強大。
本文通過一個簡單例子,介紹 Systemd 如何設定定時任務。這不僅實用,而且可以作為 Systemd 的上手教程。
一、定時任務
所謂定時任務,就是未來的某個或多個時點,預定要執行的任務,比如每五分鐘收一次郵件、每天半夜兩點分析一下日誌等等。
Linux 系統通常都使用 cron 設定定時任務,但是 Systemd 也有這個功能,而且優點顯著。
- 自動生成日誌,配合 Systemd 的日誌工具,很方便除錯
- 可以設定記憶體和 CPU 的使用額度,比如最多使用50%的 CPU
- 任務可以拆分,依賴其他 Systemd 單元,完成非常複雜的任務
下面,我就來演示一個 Systemd 定時任務:每小時傳送一封電子郵件。
二、郵件指令碼
先寫一個發郵件的指令碼mail.sh
。
#!/usr/bin/env bash echo "This is the body" | /usr/bin/mail -s "Subject" someone@example.com
上面程式碼的someone@example.com
,請替換成你的郵箱地址。
然後,執行這個指令碼。
$ bash mail.sh
執行後,你應該就會收到一封郵件,標題為Subject
。
如果你的 Linux 系統不能發郵件,建議安裝 ssmtp 或者 msmtp。另外,mail
命令的用法,可以參考這裡。
三、Systemd 單元
學習 Systemd 的第一步,就是搞懂"單元"(unit)是什麼。
簡單說,單元就是 Systemd 的最小功能單位,是單個程式的描述。一個個小的單元互相呼叫和依賴,組成一個龐大的任務管理系統,這就是 Systemd 的基本思想。
由於 Systemd 要做的事情太多,導致單元有很多不同的種類,大概一共有12種。舉例來說,Service 單元負責後臺服務,Timer 單元負責定時器,Slice 單元負責資源的分配。
每個單元都有一個單元描述檔案,它們分散在三個目錄。
/lib/systemd/system
:系統預設的單元檔案/etc/systemd/system
:使用者安裝的軟體的單元檔案/usr/lib/systemd/system
:使用者自己定義的單元檔案
下面的命令可以檢視所有的單元檔案。
# 檢視所有單元 $ systemctl list-unit-files # 檢視所有 Service 單元 $ systemctl list-unit-files --type service # 檢視所有 Timer 單元 $ systemctl list-unit-files --type timer
四、單元的管理命令
下面是常用的單元管理命令。
# 啟動單元 $ systemctl start [UnitName] # 關閉單元 $ systemctl stop [UnitName] # 重啟單元 $ systemctl restart [UnitName] # 殺死單元程式 $ systemctl kill [UnitName] # 檢視單元狀態 $ systemctl status [UnitName] # 開機自動執行該單元 $ systemctl enable [UnitName] # 關閉開機自動執行 $ systemctl disable [UnitName]
五、Service 單元
前面說過,Service 單元就是所要執行的任務,比如傳送郵件就是一種 Service。
新建 Service 非常簡單,就是在/usr/lib/systemd/system
目錄裡面新建一個檔案,比如mytimer.service
檔案,你可以寫入下面的內容。
[Unit] Description=MyTimer [Service] ExecStart=/bin/bash /path/to/mail.sh
可以看到,這個 Service 單元檔案分成兩個部分。
[Unit]
部分介紹本單元的基本資訊(即後設資料),Description
欄位給出這個單元的簡單介紹(名字叫做MyTimer
)。
[Service]
部分用來定製行為,Systemd 提供許多欄位。
ExecStart
:systemctl start
所要執行的命令ExecStop
:systemctl stop
所要執行的命令ExecReload
:systemctl reload
所要執行的命令ExecStartPre
:ExecStart
之前自動執行的命令ExecStartPost
:ExecStart
之後自動執行的命令ExecStopPost
:ExecStop
之後自動執行的命令
注意,定義的時候,所有路徑都要寫成絕對路徑,比如bash
要寫成/bin/bash
,否則 Systemd 會找不到。
現在,啟動這個 Service。
$ sudo systemctl start mytimer.service
如果一切正常,你應該就會收到一封郵件。
六、Timer 單元
Service 單元只是定義瞭如何執行任務,要定時執行這個 Service,還必須定義 Timer 單元。
/usr/lib/systemd/system
目錄裡面,新建一個mytimer.timer
檔案,寫入下面的內容。
[Unit] Description=Runs mytimer every hour [Timer] OnUnitActiveSec=1h Unit=mytimer.service [Install] WantedBy=multi-user.target
這個 Timer 單元檔案分成幾個部分。
[Unit]
部分定義後設資料。
[Timer]
部分定製定時器。Systemd 提供以下一些欄位。
OnActiveSec
:定時器生效後,多少時間開始執行任務OnBootSec
:系統啟動後,多少時間開始執行任務OnStartupSec
:Systemd 程式啟動後,多少時間開始執行任務OnUnitActiveSec
:該單元上次執行後,等多少時間再次執行OnUnitInactiveSec
: 定時器上次關閉後多少時間,再次執行OnCalendar
:基於絕對時間,而不是相對時間執行AccuracySec
:如果因為各種原因,任務必須推遲執行,推遲的最大秒數,預設是60秒Unit
:真正要執行的任務,預設是同名的帶有.service
字尾的單元Persistent
:如果設定了該欄位,即使定時器到時沒有啟動,也會自動執行相應的單元WakeSystem
:如果系統休眠,是否自動喚醒系統
上面的指令碼里面,OnUnitActiveSec=1h
表示一小時執行一次任務。其他的寫法還有OnUnitActiveSec=*-*-* 02:00:00
表示每天凌晨兩點執行,OnUnitActiveSec=Mon *-*-* 02:00:00
表示每週一凌晨兩點執行,具體請參考官方文件。
七、[Install] 和 target
mytimer.timer
檔案裡面,還有一個[Install]
部分,定義開機自啟動(systemctl enable
)和關閉開機自啟動(systemctl disable
)這個單元時,所要執行的命令。
上面指令碼中,[Install]
部分只寫了一個欄位,即WantedBy=multi-user.target
。它的意思是,如果執行了systemctl enable mytimer.timer
(只要開機,定時器自動生效),那麼該定時器歸屬於multi-user.target
。
所謂 Target 指的是一組相關程式,有點像 init 程式模式下面的啟動級別。啟動某個Target 的時候,屬於這個 Target 的所有程式都會全部啟動。
multi-user.target
是一個最常用的 Target,意為多使用者模式。也就是說,當系統以多使用者模式啟動時,就會一起啟動mytimer.timer
。它背後的操作其實很簡單,執行systemctl enable mytimer.timer
命令時,就會在multi-user.target.wants
目錄裡面建立一個符號連結,指向mytimer.timer
。
八、定時器的相關命令
下面,啟動剛剛新建的這個定時器。
$ sudo systemctl start mytimer.timer
你應該立刻就會收到郵件,然後每個小時都會收到同樣郵件。
檢視這個定時器的狀態。
$ systemctl status mytimer.timer
檢視所有正在執行的定時器。
$ systemctl list-timers
關閉這個定時器。
$ sudo systemctl stop myscript.timer
下次開機,自動執行這個定時器。
$ sudo systemctl enable myscript.timer
關閉定時器的開機自啟動。
$ sudo systemctl disable myscript.timer
九、日誌相關命令
如果發生問題,就需要檢視日誌。Systemd 的日誌功能很強,提供統一的命令。
# 檢視整個日誌 $ sudo journalctl # 檢視 mytimer.timer 的日誌 $ sudo journalctl -u mytimer.timer # 檢視 mytimer.timer 和 mytimer.service 的日誌 $ sudo journalctl -u mytimer # 從結尾開始檢視最新日誌 $ sudo journalctl -f # 從結尾開始檢視 mytimer.timer 的日誌 $ journalctl -f -u timer.timer
十、參考連結
- How to Use Systemd Timers, by Jason Graham
- Using systemd as a better cron, by luqmaan
- Getting started with systemd, by CoreOS
- systemd/Timers, by ArchWiki
- Understanding Systemd Units and Unit Files, by Justin Ellingwood
(完)