1. 程式人生 > 實用技巧 >systemd.service配置

systemd.service配置

systemd.service 中文手冊

轉載: http://www.jinbuguo.com/systemd/systemd.service.html

版權宣告
本文譯者是一位開源理念的堅定支持者,所以本文雖然不是軟體,但是遵照開源的精神釋出。

無擔保:本文譯者不保證譯文內容準確無誤,亦不承擔任何由於使用此文件所導致的損失。
自由使用:任何人都可以自由的閱讀/連結/列印此文件,無需任何附加條件。
名譽權:任何人都可以自由的轉載/引用/再創作此文件,但必須保留譯者署名並註明出處。
其他作品
本文譯者十分願意與他人分享勞動成果,如果你對我的其他翻譯作品或者技術文章有興趣,可以在如下位置檢視現有的作品集:

金步國作品集 [ http://www.jinbuguo.com/ ]
聯絡方式
由於譯者水平有限,因此不能保證譯文內容準確無誤。如果你發現了譯文中的錯誤(哪怕是錯別字也好),請來信指出,任何提高譯文質量的建議我都將虛心接納。

Email(QQ):70171448在QQ郵箱

手冊索引 · 指令索引systemd-241
名稱
systemd.service — 服務單元配置

大綱

service.service

描述
以 ".service" 為字尾的單元檔案, 封裝了一個被 systemd 監視與控制的程序。

本手冊列出了所有專用於此類單元的 配置選項(亦稱"配置指令"或"單元屬性")。 systemd.unit(5) 中描述了通用於所有單元型別的配置選項, 它們位於 "[Unit]" 與 "[Install]" 小節。 此類單元專用的配置選項 位於 "[Service]" 小節。

其他可用的選項參見 systemd.exec(5) 手冊(定義了命令的執行環境), 以及 systemd.kill(5) 手冊(定義瞭如何結束程序), 以及 systemd.resource-control(5) 手冊(定義了程序的 資源控制)。

如果要求啟動或停止的某個單元檔案 不存在, systemd 將會尋找同名的SysV初始化指令碼(去掉 .service 字尾), 並根據那個同名指令碼, 動態的建立一個 service 單元。 這主要用於與傳統的SysV相容(不能保證100%相容)。 更多與SysV的相容性可參見 Incompatibilities with SysV 文件。

服務模板
systemd 服務可以通過 "[email protected]" 語法接受一個單獨的引數。這樣的服務被稱為"例項化"服務,而不帶 argument 引數的服務單元定義被稱為"模板"。例如 [email protected] 服務模板接受一個網路介面引數之後, 就生成了一個例項化服務。在服務單元模板檔案內部, 可以通過 %-specifiers 引用引數(也稱為"例項名")。詳見 systemd.unit(5) 手冊。

自動依賴
隱含依賴
下列依賴關係是自動隱含的:

設定了 Type=dbus 的服務會自動新增 Requires=dbus.socket 與 After=dbus.socket 依賴。

基於套接字啟動的服務會自動新增對關聯的 .socket 單元的 After= 依賴。 服務單元還會為所有在 Sockets= 中列出的 .socket 單元自動新增 Wants= 與 After= 依賴。

還有一些 其他依賴關係是由 systemd.exec(5) 與 systemd.resource-control(5) 中的某些資源限制選項自動隱含新增的。

預設依賴
除非明確設定了 DefaultDependencies=no ,否則 service 單元將會自動新增下列依賴關係:

Requires=sysinit.target, After=sysinit.target, After=basic.target, Conflicts=shutdown.target, Before=shutdown.target 。 這樣可以確保普通的服務單元: (1)在基礎系統啟動完畢之後才開始啟動,(2)在關閉系統之前先被幹淨的停止。 只有那些需要在系統啟動的早期就必須啟動的服務, 以及那些必須在關機動作的結尾才能停止的服務才需要設定 DefaultDependencies=no 。

從同一個模版例項化出來的所有服務單元(單元名稱中帶有 "@" 字元), 預設全部屬於與模版同名的同一個 slice 單元(參見 systemd.slice(5))。 該同名 slice 一般在系統關機時,與所有模版例項一起停止。 如果你不希望像上面這樣,那麼可以在模版單元中明確設定 DefaultDependencies=no , 並且:要麼在該模版檔案中明確定義特定的 slice 單元(同樣也要明確設定 DefaultDependencies=no)、 要麼在該模版檔案中明確設定 Slice=system.slice (或其他合適的 slice)。 參見 systemd.resource-control(5) 手冊。

選項

每個服務單元檔案都必須包含一個 [Service] 小節。 由於此小節中的許多選項也 同時適用於其他型別的單元, 所以本手冊僅記錄了專用於服務單元的選項。 其他共享的選項參見 systemd.exec(5), systemd.kill(5), systemd.resource-control(5) 手冊。 這裡只列出僅能用於 [Service] 小節的 選項(亦稱"指令"或"屬性"):

Type=

設定程序的啟動型別。必須設為 simple, exec, forking, oneshot, dbus, notify, idle 之一:

如果設為 simple (當設定了 ExecStart= 、 但是沒有設定 Type= 與 BusName= 時,這是預設值), 那麼 ExecStart= 程序就是該服務的主程序, 並且 systemd 會認為在建立了該服務的主服務程序之後,該服務就已經啟動完成。 如果此程序需要為系統中的其他程序提供服務, 那麼必須在該服務啟動之前先建立好通訊渠道(例如套接字), 這樣,在建立主服務程序之後、執行主服務程序之前,即可啟動後繼單元, 從而加快了後繼單元的啟動速度。 這就意味著對於 simple 型別的服務來說, 即使不能成功呼叫主服務程序(例如 User= 不存在、或者二進位制可執行檔案不存在), systemctl start 也仍然會執行成功。

exec 與 simple 類似,不同之處在於, 只有在該服務的主服務程序執行完成之後,systemd 才會認為該服務啟動完成。 其他後繼單元必須一直阻塞到這個時間點之後才能繼續啟動。換句話說, simple 表示當 fork() 函式返回時,即算是啟動完成,而 exec 則表示僅在 fork() 與 execve() 函式都執行成功時,才算是啟動完成。 這就意味著對於 exec 型別的服務來說, 如果不能成功呼叫主服務程序(例如 User= 不存在、或者二進位制可執行檔案不存在), 那麼 systemctl start 將會執行失敗。

如果設為 forking ,那麼表示 ExecStart= 程序將會在啟動過程中使用 fork() 系統呼叫。 也就是當所有通訊渠道都已建好、啟動亦已成功之後,父程序將會退出,而子程序將作為主服務程序繼續執行。 這是傳統UNIX守護程序的經典做法。 在這種情況下,systemd 會認為在父程序退出之後,該服務就已經啟動完成。 如果使用了此種類型,那麼建議同時設定 PIDFile= 選項,以幫助 systemd 準確可靠的定位該服務的主程序。 systemd 將會在父程序退出之後 立即開始啟動後繼單元。

oneshot 與 simple 類似,不同之處在於, 只有在該服務的主服務程序退出之後,systemd 才會認為該服務啟動完成,才會開始啟動後繼單元。 此種類型的服務通常需要設定 RemainAfterExit= 選項。 當 Type= 與 ExecStart= 都沒有設定時, Type=oneshot 就是預設值。

dbus 與 simple 類似,不同之處在於, 該服務只有獲得了 BusName= 指定的 D-Bus 名稱之後,systemd 才會認為該服務啟動完成,才會開始啟動後繼單元。 設為此型別相當於隱含的依賴於 dbus.socket 單元。 當設定了 BusName= 時, 此型別就是預設值。

notify 與 exec 類似,不同之處在於, 該服務將會在啟動完成之後通過 sd_notify(3) 之類的介面傳送一個通知訊息。systemd 將會在啟動後繼單元之前, 首先確保該程序已經成功的傳送了這個訊息。如果設為此型別,那麼下文的 NotifyAccess= 將只能設為非 none 值。如果未設定 NotifyAccess= 選項、或者已經被明確設為 none ,那麼將會被自動強制修改為 main 。注意,目前 Type=notify 尚不能與 PrivateNetwork=yes 一起使用。

idle 與 simple 類似,不同之處在於, 服務程序將會被延遲到所有活動任務都完成之後再執行。 這樣可以避免控制檯上的狀態資訊與shell指令碼的輸出混雜在一起。 注意:(1)僅可用於改善控制檯輸出,切勿將其用於不同單元之間的排序工具; (2)延遲最多不超過5秒, 超時後將無條件的啟動服務程序。

建議對長時間持續執行的服務儘可能使用 Type=simple (這是最簡單和速度最快的選擇)。 注意,因為 simple 型別的服務 無法報告啟動失敗、也無法在服務完成初始化後對其他單元進行排序, 所以,當客戶端需要通過僅由該服務本身建立的IPC通道(而非由 systemd 建立的套接字或 D-bus 之類)連線到該服務的時候, simple 型別並不是最佳選擇。在這種情況下, notify 或 dbus(該服務必須提供 D-Bus 介面) 才是最佳選擇, 因為這兩種型別都允許服務程序精確的安排 何時算是服務啟動成功、何時可以繼續啟動後繼單元。 notify 型別需要服務程序明確使用 sd_notify() 函式或類似的API, 否則,可以使用 forking 作為替代(它支援傳統的UNIX服務啟動協議)。 最後,如果能夠確保服務程序呼叫成功、服務程序自身不做或只做很少的初始化工作(且不大可能初始化失敗), 那麼 exec 將是最佳選擇。 注意,因為使用任何 simple 之外的型別都需要等待服務完成初始化,所以可能會減慢系統啟動速度。 因此,應該儘可能避免使用 simple 之外的型別(除非必須)。另外,也不建議對長時間持續執行的服務使用 idle 或 oneshot 型別。

RemainAfterExit=

當該服務的所有程序全部退出之後, 是否依然將此服務視為活動(active)狀態。 預設值為 no

GuessMainPID=

在無法明確定位 該服務主程序的情況下, systemd 是否應該猜測主程序的PID(可能不正確)。 該選項僅在設定了 Type=forking 但未設定 PIDFile= 的情況下有意義。 如果PID猜測錯誤, 那麼該服務的失敗檢測與自動重啟功能將失效。 預設值為 yes

PIDFile=

該服務PID檔案的路徑(一般位於 /run/ 目錄下)。 強烈建議在 Type=forking 的情況下明確設定此選項。 如果設為相對路徑,那麼表示相對於 /run/ 目錄。 systemd 將會在此服務啟動完成之後,從此檔案中讀取主服務程序的PID 。 systemd 不會寫入此檔案,但會在此服務停止後刪除它(若仍然存在)。 PID檔案的擁有者不必是特權使用者, 但是如果擁有者是非特權使用者,那麼必須施加如下安全限制: (1)不能是一個指向其他擁有者檔案的軟連線(無論直接還是間接); (2)其中的PID必須指向一個屬於該服務的程序。

BusName=

設定與此服務通訊 所使用的 D-Bus 名稱。 在 Type=dbus 的情況下 必須明確設定此選項。

ExecStart=

在啟動該服務時需要執行的 命令列(命令+引數)。 有關命令列的更多細節, 可參見後文的"命令列"小節。

除非 Type=oneshot ,否則必須且只能設定一個命令列。 僅在 Type=oneshot 的情況下,才可以設定任意個命令列(包括零個), 多個命令列既可以在同一個 ExecStart= 中設定,也可以通過設定多個 ExecStart= 來達到相同的效果。 如果設為一個空字串,那麼先前設定的所有命令列都將被清空。 如果不設定任何 ExecStart= 指令, 那麼必須確保設定了 RemainAfterExit=yes 指令,並且至少設定一個 ExecStop= 指令。 同時缺少 ExecStart= 與 ExecStop= 的服務單元是非法的(也就是必須至少明確設定其中之一)。

命令列必須以一個可執行檔案(要麼是絕對路徑、要麼是不含任何斜線的檔名)開始, 並且其後的那些引數將依次作為"argv[1] argv[2] …"傳遞給被執行的程序。 可選的,可以在絕對路徑前面加上各種不同的字首表示不同的含義:

表 1. 可執行檔案前的特殊字首

字首 效果
"@" 如果在絕對路徑前加上可選的 "@" 字首,那麼其後的那些引數將依次作為"argv[0] argv[1] argv[2] …"傳遞給被執行的程序(注意,argv[0] 是可執行檔案本身)。
"-" 如果在絕對路徑前加上可選的 "-" 字首,那麼即使該程序以失敗狀態(例如非零的返回值或者出現異常)退出,也會被視為成功退出,但同時會留下錯誤日誌。
"+" 如果在絕對路徑前加上可選的 "+" 字首,那麼程序將擁有完全的許可權(超級使用者的特權),並且 User=, Group=, CapabilityBoundingSet= 選項所設定的許可權限制以及 PrivateDevices=, PrivateTmp= 等檔案系統名字空間的配置將被該命令列啟動的程序忽略(但仍然對其他 ExecStart=, ExecStop= 有效)。
"!" 與 "+" 類似(程序仍然擁有超級使用者的身份),不同之處在於僅忽略 User=, Group=, SupplementaryGroups= 選項的設定,而例如名字空間之類的其他限制依然有效。注意,當與 DynamicUser= 一起使用時,將會在執行該命令之前先動態分配一對 user/group ,然後將身份憑證的切換操作留給程序自己去執行。
"!!" 與 "!" 極其相似,僅用於讓利用 ambient capability 限制程序許可權的單元相容不支援 ambient capability 的系統(也就是不支援 AmbientCapabilities= 選項)。如果在不支援 ambient capability 的系統上使用此字首,那麼 SystemCallFilter= 與 CapabilityBoundingSet= 將被隱含的自動修改為允許程序自己丟棄 capability 與特權使用者的身份(即使原來被配置為禁止這麼做),並且 AmbientCapabilities= 選項將會被忽略。此字首在支援 ambient capability 的系統上完全沒有任何效果。

"@", "-" 以及 "+"/"!"/"!!" 之一,可以按任意順序同時混合使用。 注意,對於 "+", "!", "!!" 字首來說,僅能單獨使用三者之一,不可混合使用多個。 注意,這些字首同樣也可以用於 ExecStartPre=, ExecStartPost=, ExecReload=, ExecStop=, ExecStopPost= 這些接受命令列的選項。

如果設定了多個命令列, 那麼這些命令列將以其在單元檔案中出現的順序依次執行。 如果某個無 "-" 字首的命令列執行失敗, 那麼剩餘的命令列將不會被繼續執行, 同時該單元將變為失敗(failed)狀態。

當未設定 Type=forking 時, 這裡設定的命令列所啟動的程序 將被視為該服務的主守護程序。

ExecStartPre=, ExecStartPost=

設定在執行 ExecStart= 之前/後執行的命令列。 語法規則與 ExecStart= 完全相同。 如果設定了多個命令列, 那麼這些命令列將以其在單元檔案中出現的順序 依次執行。

如果某個無 "-" 字首的命令列執行失敗, 那麼剩餘的命令列將不會被繼續執行, 同時該單元將變為失敗(failed)狀態。

僅在所有無 "-" 字首的 ExecStartPre= 命令全部執行成功的前提下, 才會繼續執行 ExecStart= 命令。

ExecStartPost= 命令僅在 ExecStart= 中的命令已經全部執行成功之後才會執行, 判斷的標準基於 Type= 選項。 具體說來,對於 Type=simple 或 Type=idle 就是主程序已經成功啟動; 對於 Type=oneshot 來說就是最後一個 ExecStart= 程序已經成功退出; 對於 Type=forking 來說就是初始程序已經成功退出; 對於 Type=notify 來說就是已經發送了 "READY=1" ; 對於 Type=dbus 來說就是已經取得了 BusName= 中設定的匯流排名稱。

注意,不可將 ExecStartPre= 用於 需要長時間執行的程序。 因為所有由 ExecStartPre= 派生的子程序 都會在啟動 ExecStart= 服務程序之前被殺死。

注意,如果在服務啟動完成之前,任意一個 ExecStartPre=, ExecStart=, ExecStartPost= 中無 "-" 字首的命令執行失敗或超時, 那麼,ExecStopPost= 將會被繼續執行,而 ExecStop= 則會被跳過。

ExecReload=

這是一個可選的指令, 用於設定當該服務 被要求重新載入配置時 所執行的命令列。 語法規則與 ExecStart= 完全相同。

另外,還有一個特殊的環境變數 $MAINPID 可用於表示主程序的PID, 例如可以這樣使用:

/bin/kill -HUP $MAINPID
注意,像上例那樣,通過向守護程序傳送復位訊號, 強制其重新載入配置檔案,並不是一個好習慣。 因為這是一個非同步操作, 所以不適用於需要按照特定順序重新載入配置檔案的服務。 我們強烈建議將 ExecReload= 設為一個 能夠確保重新載入配置檔案的操作同步完成的命令列。

ExecStop=

這是一個可選的指令, 用於設定當該服務被要求停止時所執行的命令列。 語法規則與 ExecStart= 完全相同。 執行完此處設定的所有命令列之後,該服務將被視為已經停止, 此時,該服務所有剩餘的程序將會根據 KillMode= 的設定被殺死(參見 systemd.kill(5))。 如果未設定此選項,那麼當此服務被停止時, 該服務的所有程序都將會根據 KillSignal= 的設定被立即全部殺死。 與 ExecReload= 一樣, 也有一個特殊的環境變數 $MAINPID 可用於表示主程序的PID 。

一般來說,不應該僅僅設定一個結束服務的命令而不等待其完成。 因為當此處設定的命令執行完之後, 剩餘的程序會被按照 KillMode= 與 KillSignal= 的設定立即殺死, 這可能會導致資料丟失。 因此,這裡設定的命令必須是同步操作,而不能是非同步操作。

注意,僅在服務確實啟動成功的前提下,才會執行 ExecStop= 中設定的命令。 如果服務從未啟動或啟動失敗(例如,任意一個 ExecStart=, ExecStartPre=, ExecStartPost= 中無 "-" 字首的命令執行失敗或超時), 那麼 ExecStop= 將會被跳過。 如果想要無條件的在服務停止後執行特定的動作,那麼應該使用 ExecStopPost= 選項。 如果服務啟動成功,那麼即使主服務程序已經終止(無論是主動退出還是被殺死),也會繼續執行停止操作。 因此停止命令必須正確處理這種場景,如果 systemd 發現在呼叫停止命令時主服務程序已經終止,那麼將會撤銷 $MAINPID 變數。

重啟服務的動作被實現為"先停止、再啟動"。所以在重啟期間,將會執行 ExecStop= 與 ExecStopPost= 命令。 推薦將此選項用於那些必須在服務乾淨退出之前執行的命令(例如還需要繼續與主服務程序通訊)。當此選項設定的命令被執行的時候,應該假定服務正處於完全正常的執行狀態,可以正常的與其通訊。 如果想要無條件的在服務停止後"清理屍體",那麼應該使用 ExecStopPost= 選項。

ExecStopPost=

這是一個可選的指令, 用於設定在該服務停止之後所執行的命令列。 語法規則與 ExecStart= 完全相同。 注意,與 ExecStop= 不同,無論服務是否啟動成功, 此選項中設定的命令都會在服務停止後被無條件的執行。

應該將此選項用於設定那些無論服務是否啟動成功, 都必須在服務停止後無條件執行的清理操作。 此選項設定的命令必須能夠正確處理由於服務啟動失敗而造成的各種殘缺不全以及資料不一致的場景。 由於此選項設定的命令在執行時,整個服務的所有程序都已經全部結束, 所以無法與服務進行任何通訊。

注意,此處設定的所有命令在被呼叫之後都可以讀取如下環境變數: $SERVICE_RESULT(服務的最終結果), $EXIT_CODE(服務主程序的退出碼), $EXIT_STATUS(服務主程序的退出狀態)。 詳見 systemd.exec(5) 手冊。

RestartSec=

設定在重啟服務(Restart=)前暫停多長時間。 預設值是100毫秒(100ms)。 如果未指定時間單位,那麼將視為以秒為單位。 例如設為"20"等價於設為"20s"。

TimeoutStartSec=

設定該服務允許的最大啟動時長。 如果守護程序未能在限定的時長內發出"啟動完畢"的訊號,那麼該服務將被視為啟動失敗,並會被關閉。 如果未指定時間單位,那麼將視為以秒為單位。 例如設為"20"等價於設為"20s"。 設為 "infinity" 則表示永不超時。 當 Type=oneshot 時, 預設值為 "infinity" (永不超時), 否則預設值等於 DefaultTimeoutStartSec= 的值(參見 systemd-system.conf(5) 手冊)。

如果一個 Type=notify 服務傳送了 "EXTEND_TIMEOUT_USEC=…" 訊號, 那麼允許的啟動時長將會在 TimeoutStartSec= 基礎上繼續延長指定的時長。 注意,必須在 TimeoutStartSec= 用完之前發出第一個延時訊號。當啟動時間超出 TimeoutStartSec= 之後,該服務可以在維持始終不超時的前提下,不斷重複傳送 "EXTEND_TIMEOUT_USEC=…" 訊號, 直到完成啟動(傳送 "READY=1" 訊號)。詳見 sd_notify(3) 手冊。

TimeoutStopSec=

此選項有兩個用途: (1)設定每個 ExecStop= 的超時時長。如果其中之一超時, 那麼所有後繼的 ExecStop= 都會被取消,並且該服務也會被 SIGTERM 訊號強制關閉。 如果該服務沒有設定 ExecStop= ,那麼該服務將會立即被 SIGTERM 訊號強制關閉。 (2)設定該服務自身停止的超時時長。如果超時,那麼該服務將會立即被 SIGTERM 訊號強制關閉(參見 systemd.kill(5) 手冊中的 KillMode= 選項)。 如果未指定時間單位,那麼將視為以秒為單位。 例如設為"20"等價於設為"20s"。 設為 "infinity" 則表示永不超時。 預設值等於 DefaultTimeoutStopSec= 的值(參見 systemd-system.conf(5) 手冊)。

如果一個 Type=notify 服務傳送了 "EXTEND_TIMEOUT_USEC=…" 訊號, 那麼允許的停止時長將會在 TimeoutStopSec= 基礎上繼續延長指定的時長。 注意,必須在 TimeoutStopSec= 用完之前發出第一個延時訊號。當停止時間超出 TimeoutStopSec= 之後,該服務可以在維持始終不超時的前提下,不斷重複傳送 "EXTEND_TIMEOUT_USEC=…" 訊號,直到完成停止。 詳見 sd_notify(3) 手冊。

TimeoutSec=

一個同時設定 TimeoutStartSec= 與 TimeoutStopSec= 的快捷方式。

RuntimeMaxSec=

允許服務持續執行的最大時長。 如果服務持續執行超過了此處限制的時長,那麼該服務將會被強制終止,同時將該服務變為失敗(failed)狀態。 注意,此選項對 Type=oneshot 型別的服務無效,因為它們會在啟動完成後立即終止。 預設值為 "infinity" (不限時長)。

如果一個 Type=notify 服務傳送了 "EXTEND_TIMEOUT_USEC=…" 訊號, 那麼允許的執行時長將會在 RuntimeMaxSec= 基礎上繼續延長指定的時長。 注意,必須在 RuntimeMaxSec= 用完之前發出第一個延時訊號。當執行時間超出 RuntimeMaxSec= 之後,該服務可以在維持始終不超時的前提下,不斷重複傳送 "EXTEND_TIMEOUT_USEC=…" 訊號, 直到執行結束(傳送 "STOPPING=1" 訊號或直接退出)。詳見 sd_notify(3) 手冊。

WatchdogSec=

設定該服務的看門狗(watchdog)的超時時長。 看門狗將在服務成功啟動之後被啟動。 該服務在執行過程中必須週期性的以 "WATCHDOG=1" ("keep-alive ping")呼叫 sd_notify(3) 函式。 如果在兩次呼叫之間的時間間隔大於這裡設定的值, 那麼該服務將被視為失敗(failed)狀態, 並會被強制使用 WatchdogSignal= 訊號(預設為 SIGABRT)關閉。 通過將 Restart= 設為 on-failure, on-watchdog, on-abnormal, always 之一, 可以實現在失敗狀態下的自動重啟該服務。 這裡設定的值將會通過 WATCHDOG_USEC= 環境變數傳遞給守護程序, 這樣就允許那些支援看門狗的服務自動啟用"keep-alive ping"。 如果設定了此選項, 那麼 NotifyAccess= 將只能設為非 none 值。 如果 NotifyAccess= 未設定,或者已經被明確設為 none , 那麼將會被自動強制修改為 main 。 如果未指定時間單位,那麼將視為以秒為單位。 例如設為"20"等價於設為"20s"。 預設值"0"表示禁用看門狗功能。 詳見 sd_watchdog_enabled(3) 與 sd_event_set_watchdog(3) 手冊。

Restart=

當服務程序 正常退出、異常退出、被殺死、超時的時候, 是否重新啟動該服務。 所謂"服務程序" 是指 ExecStartPre=, ExecStartPost=, ExecStop=, ExecStopPost=, ExecReload= 中設定的程序。 當程序是由於 systemd 的正常操作(例如 systemctl stop|restart)而被停止時, 該服務不會被重新啟動。 所謂"超時"可以是看門狗的"keep-alive ping"超時, 也可以是 systemctl start|reload|stop 操作超時。

該選項的值可以取 no, on-success, on-failure, on-abnormal, on-watchdog, on-abort, always 之一。 no(預設值) 表示不會被重啟。 always 表示會被無條件的重啟。 on-success 表示僅在服務程序正常退出時重啟, 所謂"正常退出"是指:退出碼為"0", 或者程序收到 SIGHUP, SIGINT, SIGTERM, SIGPIPE 訊號之一, 並且 退出碼符合 SuccessExitStatus= 的設定。 on-failure 表示 僅在服務程序異常退出時重啟, 所謂"異常退出" 是指: 退出碼不為"0", 或者 程序被強制殺死(包括 "core dump"以及收到 SIGHUP, SIGINT, SIGTERM, SIGPIPE 之外的其他訊號), 或者程序由於 看門狗超時 或者 systemd 的操作超時 而被殺死。

表 2. Restart= 的設定分別對應於哪些退出原因

退出原因(↓) | Restart= (→) no always on-success on-failure on-abnormal on-abort on-watchdog
正常退出 X X
退出碼不為"0" X X
程序被強制殺死 X X X X
systemd 操作超時 X X X
看門狗超時 X X X X

注意如下例外情況(詳見下文): (1) RestartPreventExitStatus= 中列出的退出碼或訊號永遠不會導致該服務被重啟。 (2) 被 systemctl stop 命令或等價的操作停止的服務永遠不會被重啟。 (3) RestartForceExitStatus= 中列出的退出碼或訊號將會 無條件的導致該服務被重啟。

注意,服務的重啟頻率仍然會受到由 StartLimitIntervalSec= 與 StartLimitBurst= 定義的啟動頻率的制約。詳見 systemd.unit(5) 手冊。只有在達到啟動頻率限制之後, 重新啟動的服務才會進入失敗狀態。

對於需要長期持續執行的守護程序, 推薦設為 on-failure 以增強可用性。 對於自身可以自主選擇何時退出的服務, 推薦設為 on-abnormal

SuccessExitStatus=1 2 8 SIGKILL

表示當程序的退出碼是 1, 2, 8 或被 SIGKILL 訊號終止時, 都可以視為"正常退出"。

如果多次使用此選項, 那麼最終的結果將是多個列表的合併。 如果將此選項設為空, 那麼先前設定的列表 將被清空。

RestartPreventExitStatus=

可以設為一系列 以空格分隔的數字退出碼或訊號名稱, 當程序的退出碼或收到的訊號與此處的設定匹配時, 無論 Restart= 選項 是如何設定的, 該服務都將無條件的 禁止重新啟動。 例如:

RestartPreventExitStatus=1 6 SIGABRT

可以確保退出碼 1, 6 與 SIGABRT 訊號 不會導致該服務被自動重啟。 預設值為空, 表示完全遵守 Restart= 的設定。 如果多次使用此選項,那麼最終的結果將是多個列表的合併。 如果將此選項設為空,那麼先前設定的列表將被清空。

RestartForceExitStatus=

可以設為一系列以空格分隔的數字退出碼或訊號名稱, 當程序的退出碼或收到的訊號與此處的設定匹配時, 無論 Restart= 是如何設定的,該服務都將無條件的被自動重新啟動。 預設值為空,表示完全遵守 Restart= 的設定。 如果多次使用此選項,那麼最終的結果將是多個列表的合併。 如果將此選項設為空,那麼先前設定的列表將被清空。

RootDirectoryStartOnly=

接受一個布林值。 設為 yes 表示根目錄 RootDirectory= 選項(參見 systemd.exec(5) 手冊) 僅對 ExecStart= 中的程式有效, 而對 ExecStartPre=, ExecStartPost=, ExecReload=, ExecStop=, ExecStopPost= 中的程式無效。 預設值 no 表示根目錄對所有 Exec*= 系列選項中的程式都有效。

NonBlocking=

是否為所有基於套接字啟動傳遞的檔案描述符設定非阻塞標記(O_NONBLOCK)。 設為 yes 表示除了通過 FileDescriptorStoreMax= 引入的檔案描述符之外, 所有 ≥3 的檔案描述符(非 stdin, stdout, stderr 檔案描述符)都將被設為非阻塞模式。 該選項僅在與 socket 單元 (systemd.socket(5)) 聯用的時候才有意義。 對於那些先前已經通過 FileDescriptorStoreMax= 引入的檔案描述符則毫無影響。 預設值為 no

NotifyAccess=

設定通過 sd_notify(3) 訪問服務狀態通知套接字的模式。 可以設為 none(預設值), main, exec, all 之一。 none 表示不更新任何守護程序的狀態,忽略所有狀態更新訊息。 main 表示僅接受主程序的狀態更新訊息。 exec 表示僅接受主程序以及 Exec*= 程序的狀態更新訊息。 all 表示接受該服務cgroup內所有程序的狀態更新訊息。 當設定了 Type=notify 或 WatchdogSec= 的時候(見前文),此選項將只能設為非 none 值。 如果 NotifyAccess= 未設定,或者已經被明確設為 none , 那麼將會被自動強制修改為 main 。

注意,服務單元的 sd_notify() 通知能夠正常工作的前提, 是必須滿足如下兩個條件之一: (1)在 PID=1 的程序處理通知訊息時,傳送該通知的程序依然在執行; (2)傳送該通知的程序是 systemd 派生的子程序(也就是匹配 main 或 exec 的程序)。 如果服務單元中的某個輔助程序在傳送了 sd_notify() 通知之後就立即退出了, 那麼 systemd 將有可能來不及將該通知關聯到這個服務單元上。 在這種情況下,即使明確設定了 NotifyAccess=all , 該通知也可能會被忽略掉。

Sockets=

設定一個 socket 單元的名稱, 表示該服務在啟動時應當從它繼承套接字檔案描述符。 通常並不需要明確設定此選項, 因為所有與該服務同名(不算字尾)的 socket 單元的套接字檔案描述符, 都會被自動的 傳遞給派生程序。

注意: (1)同一個套接字檔案描述符可以被傳遞給多個不同的程序(服務)。 (2)當套接字上有流量進入時, 被啟動的可能是另一個不同於該服務的其他服務。 換句話說就是: 套接字單元中的 Sockets= 所指向的服務單元中的 Sockets= 未必要反向指回去。

如果多次使用此選項, 那麼最終的結果將是多個socket單元的合集。 如果將此選項設為空, 那麼先前設定的所有socket單元 都將被清空。

FileDescriptorStoreMax=

允許在 systemd 中最多為該服務儲存多少個使用 "FDSTORE=1" 訊息(sd_pid_notify_with_fds(3)) 的檔案描述符。預設值為"0"(不儲存)。 通過將服務重啟過程中不應該關閉的套接字與檔案描述符使用這種方法儲存起來, 就可以實現讓服務在重啟(正常重啟或崩潰重啟)之後不丟失其狀態。 程序的狀態可以被序列化為一個檔案之後儲存在 /run 中, 或者儲存在一個 memfd_create(2) 記憶體檔案描述符中(這是更好的選擇)。 所有被 systemd 暫存的檔案描述符都將在該服務重啟之後交還給該服務的主程序。 所有被 systemd 暫存的檔案描述符都將在遇到如下兩種情況時被自動關閉: (1)收到 POLLHUP 或 POLLERR 訊號; (2)該服務被徹底停止,並且沒有任何剩餘的任務需要處理。 如果使用了此選項,那麼前文的 NotifyAccess= 應該被設為允許訪問 systemd 提供的通知套接字。若未設定 NotifyAccess= ,那麼將被隱含的設為 main

USBFunctionDescriptors=

設為一個包含 USB FunctionFS 描述符的檔案路徑, 以實現 USB gadget 支援。 僅與配置了 ListenUSBFunction= 的 socket 單元一起使用。該檔案的內容將被寫入 ep0 檔案。

USBFunctionStrings=

設為一個包含 USB FunctionFS 字串的檔案路徑。 其行為與上面的 USBFunctionDescriptors= 類似。

參見 systemd.exec(5) 與 systemd.kill(5) 手冊,以瞭解更多其他選項。

命令列

本小節 講解 ExecStart=, ExecStartPre=, ExecStartPost=, ExecReload=, ExecStop=, ExecStopPost= 選項的命令列解析規則。

如果要一次設定多個命令,那麼可以使用分號(;)將多個命令列連線起來。 注意,僅在設定了 Type=oneshot 的前提下,才可以一次設定多個命令。 分號自身 必須用 ";" 表示。

每個命令列的內部 都以空格分隔, 第一項是要執行的命令, 隨後的各項則是命令的引數。 每一項的邊界都可以用單引號或雙引號界定, 但引號自身最終將會被剝離。 還可以使用C語言風格的轉義序列, 但僅可使用下文表格中的轉義序列。 最後,行尾的反斜槓("") 將被視作續行符(借鑑了bash續行語法)。

命令列的語法刻意借鑑了shell中的轉義字元與變數展開語法, 但兩者並不完全相同。 特別的, 重定向("<", "<<", ">", ">>")、 管道("|")、 後臺執行("&"), 以及其他下文未明確提及的符號都不被支援。

要執行的命令(第一項)可以包含空格,但是不能包含控制字元。

可以在各項命令引數中使用 "%" 系列替換標記(詳見 systemd.unit(5)手冊)。

支援 "${FOO}" 與 "$FOO" 兩種不同的環境變數替換方式。 具體說來就是: "${FOO}" 的內容將原封不動的轉化為一個單獨的命令列引數, 無論其中是否包含空格與引號,也無論它是否為空。 "$FOO" 的內容將被原封不動的插入命令列中, 但對插入內容的解釋卻遵守一般的命令列解析規則。 後文的兩個例子, 將能清晰的體現兩者的差別。

如果要執行的命令(第一項)不是一個絕對路徑, 那麼將會在編譯時設定的可執行檔案搜尋目錄中查詢。 因為預設包括 /usr/local/bin/, /usr/bin/, /bin/, /usr/local/sbin/, /usr/sbin/, /sbin/ 目錄, 所以可以安全的直接使用"標準目錄"中的可執行程式名稱(沒必要再使用絕對路徑), 而對於非標準目錄中的可執行程式,則必須使用絕對路徑。建議始終使用絕對路徑以避免歧義。 [提示]可以使用 systemd-path search-binaries-default 顯示編譯時設定的可執行檔案搜尋目錄。

例(1):

Environment="ONE=one" 'TWO=two two'
ExecStart=echo $ONE $TWO ${TWO}
這將給 /bin/echo 命令依次傳遞如下四個引數: "one", "two", "two", "two two"

例(2):

Environment=ONE='one' "TWO='two two' too" THREE=
ExecStart=/bin/echo ${ONE} ${TWO} ${THREE}
ExecStart=/bin/echo $ONE $TWO $THREE
這將導致 /bin/echo 被執行兩次。 第一次被依次傳遞如下三個引數: "'one'", "'two two' too", "" ; 第二次被依次傳遞如下三個引數: "one", "two two", "too" 。

此外,如果想要傳遞美元符號($)自身, 則必須使用 "$$" 。 而那些無法在替換時確定內容的變數將被當做空字串。 注意,不可以在第一項(也就是命令的絕對路徑)中使用變數替換。

注意,這裡使用的變數必須已經在 Environment= 或 EnvironmentFile= 中定義。 此外,在 systemd.exec(5) 手冊的"環境變數"小節中列出的"靜態變數"也可以使用。 例如 $USER 就是一個"靜態變數", 而 $TERM 則不是。

注意, 這裡的命令列並不直接支援shell命令, 但是可以通過模仿下面這個變通的方法來實現:

ExecStart=sh -c 'dmesg | tac'
例一

ExecStart=echo one ; echo "two two"
這將導致 echo 被執行兩次。 第一次被傳遞了單獨一個 "one" 引數; 第二次被傳遞了單獨一個 "two two" 引數。 因為一次設定了多個命令,所以僅能用於 Type=oneshot 型別。

例二

ExecStart=echo / >/dev/null & ;
ls
這表示向 echo 命令傳遞五個引數: "/", ">/dev/null", "&", ";", "ls"

表 3. 可以在命令列與環境變數中使用的C語言風格的轉義序列

轉義序列 實際含義
"\a" 響鈴
"\b" 退格
"\f" 換頁
"\n" 換行
"\r" 回車
"\t" 製表符
"\v" 縱向製表符
"\" 反斜線
""" 雙引號
"'" 單引號
"\s" 空白
"\xxx" 十六進位制數 xx 所對應的字元
"\nnn" 八進位制數 nnn 所對應的字元

例子

例 1. 簡單服務

下面的單元檔案建立了一個執行 /usr/sbin/foo-daemon 守護程序的服務。 未設定 Type= 等價於 Type=simple 預設設定。 systemd 執行守護程序之後, 即認為該單元已經啟動成功。

[Unit]
Description=簡單的Foo服務

[Service]
ExecStart=/usr/sbin/foo-daemon

[Install]
WantedBy=multi-user.target
注意,本例中的 /usr/sbin/foo-daemon 必須在啟動後持續執行到服務被停止。 如果該程序只是為了派生守護程序,那麼應該使用 Type=forking

因為沒有設定 ExecStop= 選項, 所以在停止服務時,systemd 將會直接向該服務啟動的所有程序傳送 SIGTERM 訊號。 若超過指定時間依然存在未被殺死的程序,那麼將會繼續傳送 SIGKILL 訊號。 詳見 systemd.kill(5) 手冊。

預設的 Type=simple 並不包含任何通知機制(例如通知"服務啟動成功")。 要想使用通知機制,應該將 Type= 設為其他非預設值: Type=notify 可用於能夠理解 systemd 通知協議的服務; Type=forking 可用於能將自身切換到後臺的服務; Type=dbus 可用於能夠在完成初始化之後 獲得一個 D-Bus 名稱的單元。

例 2. 一次性服務

Type=oneshot 用於那些只需要執行一次性動作而不需要持久執行的單元, 例如檔案系統檢查或者清理臨時檔案。 此類單元, 將會在啟動後一直等待指定的動作完成, 然後再回到停止狀態。 下面是一個執行清理動作的單元:

[Unit]
Description=清理老舊的 Foo 資料

[Service]
Type=oneshot
ExecStart=/usr/sbin/foo-cleanup

[Install]
WantedBy=multi-user.target
注意,在 /usr/sbin/foo-cleanup 執行結束前, 該服務一直處於"啟動中"(activating)狀態,而一旦執行結束,該服務又立即變為"停止"(inactive)狀態。 也就是說,對於 Type=oneshot 型別的服務,不存在"活動"(active)狀態。 這意味著,如果再一次啟動該服務,將會再一次執行該服務定義的動作。 注意,在先後順序上晚於該服務的單元, 將會一直等到該服務變成"停止"(inactive)狀態後, 才會開始啟動。

Type=oneshot 是唯一可以設定多個 ExecStart= 指令的服務型別。 多個 ExecStart= 指令將按照它們出現的順序依次執行, 一旦遇到錯誤,就會立即停止,不再繼續執行, 同時該服務也將進入"失敗"(failed)狀態。

例 3. 可停止的一次性服務

有時候, 單元需要執行一個程式以完成某個設定(啟動), 然後又需要再執行另一個程式以撤消先前的設定(停止), 而在設定持續有效的時段中,該單元應該視為處於"活動"(active)狀態, 但實際上並無任何程式在持續執行。 網路配置服務就是一個典型的例子。 此外,只能啟動一次(不可多次啟動)的一次性服務, 也是一個例子。

可以通過設定 RemainAfterExit=yes 來滿足這種需求。 在這種情況下,systemd 將會在啟動成功後將該單元視為處於"活動"(active)狀態(而不是"停止"(inactive)狀態)。 RemainAfterExit=yes 雖然可以用於所有 Type= 型別, 但是在實踐中主要用於 Type=oneshot 和 Type=simple 型別。 對於 Type=oneshot 型別, systemd 一直等到服務啟動成功之後,才會將該服務置於"活動"(active)狀態。 所以,依賴於該服務的其他單元必須等待該服務啟動成功之後,才能啟動。 但是對於 Type=simple 型別, 依賴於該服務的其他單元無需等待, 將會和該服務同時並行啟動。 下面的類似展示了一個簡單的靜態防火牆服務:

[Unit]
Description=簡單的靜態防火牆

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/simple-firewall-start
ExecStop=/usr/local/sbin/simple-firewall-stop

[Install]
WantedBy=multi-user.target
因為服務啟動成功後一直處於"活動"(active)狀態, 所以再次執行 systemctl start 命令不會有任何效果。

例 4. 傳統的服務

多數傳統的守護程序(服務)在啟動時會轉入後臺執行。 systemd 通過 Type=forking 來支援這種工作方式。 對於這種型別的服務,如果最初啟動的程序尚未退出, 那麼該單元將依然處於"啟動中"(activating)狀態。 當最初的程序成功退出, 並且至少有一個程序仍然在執行(並且 RemainAfterExit=no), 該服務才會被視為處於"活動"(active)狀態。

對於單程序的傳統服務,當最初的程序成功退出後, 將會只剩單獨一個程序仍然在持續執行, systemd 將會把這個唯一剩餘的程序視為該服務的主程序。 僅在這種情況下,才將可以在 ExecReload=, ExecStop= … 之類的選項中使用 $MAINPID 變數。

對於多程序的傳統服務,當最初的程序成功退出後,將會剩餘多個程序在持續執行, 因此,systemd 無法確定哪一個程序才是該服務的主程序。 在這種情況下,不可以使用 $MAINPID 變數。 然而,如果主程序會建立傳統的PID檔案, 那麼應該將 PIDFile= 設為此PID檔案的絕對路徑, 以幫助 systemd 從該PID檔案中讀取主程序的PID,從而幫助確定該服務的主程序。 注意,守護程序必須在完成初始化之前寫入PID檔案, 否則可能會導致 systemd 讀取失敗 (讀取時檔案不存在)。

下面是一個 單程序傳統服務的示例:

[Unit]
Description=一個單程序傳統服務

[Service]
Type=forking
ExecStart=/usr/sbin/my-simple-daemon -d

[Install]
WantedBy=multi-user.target
參見 systemd.kill(5) 以瞭解如何 結束服務程序。

例 5. DBus 服務

對於需要在 D-Bus 系統總線上註冊一個名字的服務, 應該使用 Type=dbus 並且設定相應的 BusName= 值。 該服務不可以派生任何子程序。 一旦從 D-Bus 系統匯流排成功獲取所需的名字,該服務即被視為初始化成功。 下面是一個典型的 D-Bus 服務:

[Unit]
Description=一個簡單的 DBus 服務

[Service]
Type=dbus
BusName=org.example.simple-dbus-service
ExecStart=/usr/sbin/simple-dbus-service

[Install]
WantedBy=multi-user.target
對於基於 D-Bus 啟動的服務 來說, 不可以包含 "[Install]" 小節, 而是應該在對應的 D-Bus service 檔案中設定 SystemdService= 選項,例如 (/usr/share/dbus-1/system-services/org.example.simple-dbus-service.service):

[D-BUS Service]
Name=org.example.simple-dbus-service
Exec=/usr/sbin/simple-dbus-service
User=root
SystemdService=simple-dbus-service.service
參見 systemd.kill(5) 手冊以瞭解如何 結束服務程序。

例 6. 能夠通知初始化已完成的服務

Type=simple 型別的服務 非常容易編寫, 但是, 無法向 systemd 及時通知 "啟動成功"的訊息, 是一個重大缺陷。 Type=notify 可以彌補該缺陷, 它支援將"啟動成功"的訊息及時通知給 systemd 。 下面是一個典型的例子:

[Unit]
Description=Simple notifying service

[Service]
Type=notify
ExecStart=/usr/sbin/simple-notifying-service

[Install]
WantedBy=multi-user.target
注意, 該守護程序必須支援 systemd 通知協議, 否則 systemd 將會認為該服務一直處於"啟動中"(activating)狀態,並在超時後將其殺死。 關於如何支援該通知協議,參見 sd_notify(3) 手冊。

參見 systemd.kill(5) 手冊以瞭解如何 結束服務程序。

參見
systemd(1), systemctl(1), systemd-system.conf(5), systemd.unit(5), systemd.exec(5), systemd.resource-control(5), systemd.kill(5), systemd.directives(7)