CentOS 7之Systemd詳解之服務單元設定system.service
英文網址:https://www.freedesktop.org/software/systemd/man/systemd.service.html
名稱
systemd.service - 服務單元配置
概要
service.service
描述
以 .service 結尾的單元檔案,用於封裝一個被 systemd 監視與控制的程序。
本手冊只列出專用於此種單元的選項,它們全部位於"[Service]"小節之中(通用於所有單元的選項參見 systemd.unit(5) 手冊)。
其他可用的選項位於 systemd.exec(5) 手冊(定義了命令的執行環境), 以及 systemd.kill(5) 手冊(定義瞭如何結束程序), 以及 systemd.resource-control(5) 手冊(定義了程序的資源控制)。
如果要求啟動或停止的某個單元檔案不存在,systemd 將會尋找同名的SysV初始化指令碼(去掉 .service 字尾), 並根據那個同名指令碼動態的建立一個 service 單元。這主要用於與傳統的SysV相容(不能保證100%相容)。 更多與SysV的相容性可參見 Incompatibilities with SysV 文件。
自動依賴
- 設定了 Type=dbus 的服務會自動新增 Requires=dbus.socket 與 After=dbus.socket 依賴
- 基於套接字啟用的服務會自動新增對與其相關的 .socket 單元的 After= 依賴。
除非明確設定了 DefaultDependencies=false,否則 service 單元都自動隱含如下依賴:
- Requires=sysinit.target After=sysinit.target After=basic.target Conflicts=shutdown.target Before=shutdown.target 這樣可以確保普通的服務單元:(1)、在基礎系統啟動完畢之後才開始啟動,(2)、在關閉系統之前先被幹淨的停止。 只有那些需要在系統啟動的早期就必須啟動的服務,以及那些必須在關機動作的結尾才能停止的服務才需要設定 DefaultDependencies=false 。 systemd.exec(5) 與 systemd.resource-control(5) 中的某些資源限制選項也會自動隱含的新增一些其他的依賴關係。
- 從同一個模版例項化出來的所有服務單元(單元名稱中帶有 "
@
" 字元), 預設全部屬於與模版同名的同一個 slice 單元。 該同名 slice 一般在系統關機時,與所有模版例項一起停止。 如果你不希望像上面這樣,那麼可以在模版單元中明確設定DefaultDependencies=no
, 並且:要麼在該模版檔案中明確定義特定的 slice 單元(同樣也要明確設定DefaultDependencies=no
)、 要麼在該模版檔案中明確設定Slice=system.slice
(或其他合適的 slice)。
[Service]小節選項
每個服務單元檔案都必須包含一個"[Service]"小節。由於此小節中的許多選項也同時適用於其他型別的單元, 所以本手冊僅記錄了專用於服務單元的選項。其他共享的選項參見 systemd.exec(5) 與 systemd.kill(5) 手冊。
Type=
- 設定程序的啟動型別,必須是下列值之一:simple, forking, oneshot, dbus, notify, idle 之一。
- 如果設為"simple"(設定了 ExecStart= 但未設定 BusName= 時的預設值),那麼表示 ExecStart= 所設定的程序就是該服務的主程序。 如果此程序需要為其他程序提供服務,那麼必須在該程序啟動之前先建立好通訊渠道(例如套接字),以加快後繼單元的啟動速度。
- "dbus"(設定了 ExecStart= 與 BusName= 時的預設值)與"simple"類似,不同之處在於該程序需要在 D-Bus 上獲得一個由 BusName= 指定的名稱。 systemd 將會在啟動後繼單元之前,首先確保該程序已經成功的獲取了指定的 D-Bus 名稱。設定為此型別相當於隱含的依賴於 dbus.socket 單元。
- "oneshot"(未設定 ExecStart= 時的預設值)與"simple"類似,不同之處在於該程序必須在 systemd 啟動後繼單元之前退出。 此種類型通常需要設定 RemainAfterExit= 選項。
- 如果設為"forking",那麼表示 ExecStart= 所設定的程序將會在啟動過程中使用 fork() 系統呼叫。這是傳統UNIX守護程序的經典做法。 也就是當所有的通訊渠道都已建好、啟動亦已成功之後,父程序將會退出,而子程序將作為該服務的主程序繼續執行。 對於此種程序,建議同時設定 PIDFile= 選項,以幫助 systemd 準確定位該服務的主程序,進而加快後繼單元的啟動速度。
- "notify"與"simple"類似,不同之處在於該程序將會在啟動完成之後通過 sd_notify(3) 之類的介面傳送一個通知訊息。 systemd 將會在啟動後繼單元之前,首先確保該程序已經成功的傳送了這個訊息。 如果設定為此型別,那麼 NotifyAccess= 將只能設定為"all"或者"main"(預設)。 注意,目前 Type=notify 尚不能在 PrivateNetwork=yes 的情況下正常工作。
- "idle"與"simple"類似,不同之處在於該程序將會被延遲到所有的操作都完成之後再執行。 這樣可以避免控制檯上的狀態資訊與 shell 指令碼的輸出混雜在一起。
RemainAfterExit=
- 可設為"yes"或"no"(預設值),表示當該服務的所有程序全部退出之後,是否依然將此服務視為活動(active)狀態。
GuessMainPID=
- 可設為"yes"(預設值)或"no",表示在無法明確定位該服務的主程序的情況下,systemd 是否應該猜測主程序的PID(可能不正確)。 該選項僅在設定了 Type=forking 但未設定 PIDFile= 的情況下有意義。如果PID猜測錯誤,那麼該服務的失敗檢測與自動重啟功能將失效。
PIDFile=
- 守護程序的PID檔案,必須是絕對路徑。強烈建議在 Type=forking 的情況下明確設定此選項。 systemd 將會在此服務啟動後從此檔案中讀取主守護程序的PID 。systemd 不會寫入此檔案,但會在此服務停止後刪除它(若存在)。
BusName=
- 設定與此服務通訊所使用的 D-Bus 名稱。在 Type=dbus 的情況下必須明確設定此選項。
BusPolicy=
- 如果設定了此項,那麼 systemd 將會建立一個自定義的kdbus端點(endpoint),並將其安裝為該服務預設的匯流排節點(bus node)。 這個自定義的端點可以擁有它自己的策略規則。端點的名稱就是其所服務的單元的名稱。 端點的節點(node)將被繫結掛載到預設的匯流排節點的位置,這樣該服務就只能通過它自己的端點訪問匯流排。 注意,自定義端點的預設策略是'拒絕所有',因此,你必須在 BusPolicy= 中明確的新增必要的允許策略。 這個選項的值由兩部分組成:匯流排名+訪問級別,中間以空格分隔。 訪問級別必須是 see, talk, own 之一,並且 talk 隱含了 see ,而 own 隱含了 talk 與 see 。 如果對同一個匯流排名稱多次指定了訪問級別,那麼將以擁有最大許可權的那個為準。 例如: BusPolicy=org.freedesktop.systemd1 talk BusPolicy=org.foo.bar see 該選項僅在核心開啟了kdbus(即將併入官方核心)支援的情況下有意義。
ExecStart=
- 在啟動該服務時需要執行的命令列(命令+引數)。有關命令列的更多細節可參見後文的"命令列"小節。 僅在設定了 Type=oneshot 的情況下,才可以設定任意個命令列(包括零個),否則必須且只能設定一個命令列。 多個命令列既可以在同一個 ExecStart= 中設定,也可以通過設定多個 ExecStart= 來達到相同的效果。 如果設為一個空字串,那麼先前設定的所有命令列都將被清空。 如果不設定任何 ExecStart= 指令,那麼必須確保設定了 RemainAfterExit=yes 指令。 命令列必須以一個絕對路徑表示的可執行檔案開始,並且其後的那些引數將依次作為"argv[1] argv[2] ..."傳遞給被執行的程序。 如果在絕對路徑前加上可選的"@"字首,那麼其後的那些引數將依次作為"argv[0] argv[1] argv[2] ..."傳遞給被執行的程序。 如果在絕對路徑前加上可選的"-"字首,那麼即使該程序以失敗狀態(例如非零的返回值或者出現異常)退出,也會被視為成功退出。 可以同時使用"-"與"@"字首,且順序任意。 如果設定了多個命令列,那麼這些命令列將以其在單元檔案中出現的順序依次執行。 如果某個無"-"字首的命令列執行失敗,那麼剩餘的命令列將不會被執行,同時該單元將變為失敗(failed)狀態。 當未設定 Type=forking 時,這裡設定的命令列所啟動的程序將被視為該服務的主守護程序。
ExecStartPre=, ExecStartPost=
- 設定在執行 ExecStart= 之前/後執行的命令列。語法規則與 ExecStart= 完全相同。 如果設定了多個命令列,那麼這些命令列將以其在單元檔案中出現的順序依次執行。 如果某個無"-"字首的命令列執行失敗,那麼剩餘的命令列將不會被執行,同時該單元將變為失敗(failed)狀態。 僅在所有無"-"字首的 ExecStartPre= 命令全部執行成功的前提下,才會繼續執行 ExecStart= 命令。 ExecStartPost= 命令僅在服務已經被成功啟動之後才會執行,判斷的標準基於 Type= 選項。 具體說來,對於 Type=simple 或 Type=idle 就是主程序已經成功啟動;對於 Type=oneshot 來說就是主程序已經成功退出; 對於 Type=forking 來說就是初始程序已經成功退出;對於 Type=notify 來說就是已經發送了"READY=1"; 對於 Type=dbus 來說就是已經取得了 BusName= 中設定的匯流排名稱。 注意,不可將 ExecStartPre= 用於需要長時間執行的程序。 因為所有由 ExecStartPre= 派生的子程序都會在啟動 ExecStart= 服務程序之前被殺死。
ExecReload=
- 這是一個可選的指令,用於設定當該服務被要求重新載入配置時所執行的命令列。語法規則與 ExecStart= 完全相同。 另外,還有一個特殊的環境變數 $MAINPID 可以用於表示主程序的PID,例如可以這樣使用: /bin/kill -HUP $MAINPID 注意,像上例那樣,通過向守護程序傳送復位訊號,強制其重新載入配置檔案,並不是一個好習慣。 因為這是一個非同步操作,所以不適用於需要按照特定順序重新載入配置檔案的服務。 我們強烈建議將 ExecReload= 設定為一個能夠確保重新載入配置檔案的操作同步完成的命令列。
ExecStop=
- 這是一個可選的指令,用於設定當該服務被要求停止時所執行的命令列。語法規則與 ExecStart= 完全相同。 執行完此處設定的命令列之後,該服務所有剩餘的程序將會根據 KillMode= 的設定被殺死(參見 systemd.kill(5) 手冊)。 如果未設定此選項,那麼當此服務被停止時,該服務的所有程序都將會根據 KillMode= 的設定被立即全部殺死。 與 ExecReload= 一樣,也有一個特殊的環境變數 $MAINPID 可以用於表示主程序的PID 一般來說,僅僅設定一個結束服務的命令,而不等待其完成,是不夠的。 因為當此處設定的命令執行完之後,剩餘的程序會被 SIGKILL 訊號立即殺死,這可能會導致資料丟失。 因此,這裡設定的命令必須是同步操作,而不能是非同步操作。
ExecStopPost=
- 這是一個可選的指令,用於設定該服務停止之後所執行的命令列。語法規則與 ExecStart= 完全相同。 無論此服務是正常停止,還是異常退出,此處的設定都適用。 RestartSec= 設定在重啟服務(Restart=)前暫停多長時間。預設值是100毫秒(100ms)。 如果未指定時間單位,那麼將視為以秒為單位。例如設為"20"等價於設為"20s"。
TimeoutStartSec=
- 設定該服務允許的最大啟動時長。如果守護程序未能在限定的時長內發出"啟動完畢"的訊號,那麼該服務將被視為啟動失敗,並會被關閉。 如果未指定時間單位,那麼將視為以秒為單位。例如設為"20"等價於設為"20s"。設為"0"則表示永不超時。 當 Type=oneshot 時,預設值為"0",否則預設值等於 DefaultTimeoutStartSec= 的值(參見 systemd-system.conf(5) 手冊)。
TimeoutStopSec=
- 設定該服務允許的最大停止時長。如果該服務未能在限定的時長內成功停止,那麼將會被強制使用 SIGTERM 訊號關閉, 如果依然未能在相同的時長內成功停止,那麼將會被強制使用 SIGKILL 訊號關閉(參見 systemd.kill(5) 手冊中的 KillMode= 選項)。 如果未指定時間單位,那麼將視為以秒為單位。例如設為"20"等價於設為"20s"。設為"0"則表示永不超時。 預設值等於 DefaultTimeoutStartSec= 的值(參見 systemd-system.conf(5) 手冊)。
TimeoutSec=
- 一個同時設定 TimeoutStartSec= 與 TimeoutStopSec= 的快捷方式。
WatchdogSec=
設定該服務的看門狗(watchdog)的超時時長。看門狗將在服務成功啟動之後被啟用。 該服務在執行過程中必須週期性的以"WATCHDOG=1"("keep-alive ping")呼叫 sd_notify(3) 函式。 如果在兩次呼叫之間的時間間隔大於這裡設定的值,那麼該服務將被視為失敗(failed)狀態,並會被強制使用 SIGABRT 訊號關閉。 通過將 Restart= 設為"on-failure"或"always"可以實現在失敗狀態下的自動重啟該服務。 這裡設定的值將會通過 WATCHDOG_USEC 環境變數傳遞給守護程序,這樣就允許那些支援看門狗的服務自動啟用"keep-alive ping"。 如果設定了此選項,那麼必須將 NotifyAccess= 設為"main"(此種情況下的隱含預設值)或"all"。 如果未指定時間單位,那麼將視為以秒為單位。例如設為"20"等價於設為"20s"。預設值"0"表示禁用看門狗功能。
Restart=
- 當服務程序正常退出、異常退出、被殺死、超時的時候,是否重新啟動該服務。 "服務程序"是指 ExecStart=, 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 的操作超時而被殺死。 對於 on-failure, on-abnormal, on-abort, on-watchdog 的分別適用於哪種異常退出,見下表:
- 注意如下兩個例外情況: (1) RestartPreventExitStatus= 中列出的退出碼或者訊號永遠不會導致該服務被重啟。 (2) RestartForceExitStatus= 中列出的退出碼或者訊號將會無條件的導致該服務被重啟。 對於需要長期持續執行的守護程序,推薦設為"on-failure"以增強可用性。 對於自身可以自主選擇何時退出的服務,推薦設為"on-abnormal"。
SuccessExitStatus=
- 額外定義附加的程序"正常退出"狀態。可以設為一系列以空格分隔的數字退出碼或者訊號名稱,例如: SuccessExitStatus=1 2 8 SIGKILL 表示當程序的退出碼是 1, 2, 8 或被 SIGKILL 訊號終止時,都可以視為"正常退出"。 注意,退出碼"0"以及 SIGHUP, SIGINT, SIGTERM, SIGPIPE 訊號是標準的"正常退出",不需要在此特別定義。 注意,如果程序擁有自定義的訊號處理器,並且在收到訊號後通過呼叫 _exit(2) 退出,那麼有關訊號的資訊就會丟失。 在這種情況下,程序必須自己完成清理工作並使用相同的訊號自殺。參見 Proper handling of SIGINT/SIGQUIT — How to be a proper program 如果多次使用此選項,那麼最終的結果將是多個列表的合集。如果將此項設為空,那麼先前設定的列表將被清空。
RestartPreventExitStatus=
- 可以設為一系列以空格分隔的數字退出碼或者訊號名稱,當程序的退出碼或收到的訊號與此處的設定匹配時, 該服務將無條件的禁止重新啟動(無視 Restart= 的設定)。例如: RestartPreventExitStatus=1 6 SIGABRT 表示退出碼 1, 2, 8 與 SIGKILL 訊號將不會導致該服務被重啟。 預設值為空,表示完全遵守 Restart= 的設定。
- 如果多次使用此選項,那麼最終的結果將是多個列表的合集。如果將此項設為空,那麼先前設定的列表將被清空。 RestartForceExitStatus= 可以設為一系列以空格分隔的數字退出碼或者訊號名稱,當程序的退出碼或收到的訊號與此處的設定匹配時, 該服務將無條件的被重新啟動(無視 Restart= 的設定)。 預設值為空,表示完全遵守 Restart= 的設定。 如果多次使用此選項,那麼最終的結果將是多個列表的合集。如果將此項設為空,那麼先前設定的列表將被清空。
PermissionsStartOnly=
- 設為 true 表示所有與許可權相關的執行選項(例如 User= 之類的選項,參見 systemd.exec(5) 手冊)僅對 ExecStart= 中的程式有效, 而對 ExecStartPre=, ExecStartPost=, ExecReload=, ExecStop=, ExecStopPost= 中的程式無效。 預設值 false 表示所有與許可權相關的執行選項對所有 Exec*= 系列選項中的程式都有效。
RootDirectoryStartOnly=
- 設為 true 表示根目錄(參見 systemd.exec(5) 中的 RootDirectory= 選項)僅對 ExecStart= 中的程式有效, 而對 ExecStartPre=, ExecStartPost=, ExecReload=, ExecStop=, ExecStopPost= 中的程式無效。 預設值 false 表示根目錄對所有 Exec*= 系列選項中的程式都有效。
NonBlocking=
- 是否為所有通過socket啟用傳遞的檔案描述符設定非阻塞標記(O_NONBLOCK)。預設值為 false 設為 true 表示所有大於2的檔案描述符(也就是 stdin, stdout, stderr 之外的檔案描述符)都將被設定為非阻塞模式。 該選項僅在與 socket 單元(systemd.socket(5))聯用的時候才有意義。
NotifyAccess=
- 設定通過sd_notify(3)訪問服務狀態通知socket的訪問模式。 可以設為:none(預設值), main, all 之一。 "none"表示不更新任何守護程序的狀態,忽略所有的狀態更新訊息。 "main"表示僅接受主程序的狀態更新訊息。 "all"表示接受該服務cgroup內的所有程序的狀態更新訊息。 當設定了 Type=notify 或 WatchdogSec= 的時候,此選項應該被設為"main"或"all",如果未設定,那麼隱含為"main"。
Sockets=
- 設定一個socket單元的名稱,表示該服務在啟動時應當從它繼承socket檔案描述符。通常並不需要明確設定此選項, 因為所有與該服務同名(不算字尾)的socket單元的socket檔案描述符,都會被自動的傳遞給派生程序。 注意:(1)同一個socket檔案描述符可以被傳遞給多個不同的程序(服務)。 (2)當socket上有流量進入時,被啟動的可能是另一個不同於該服務的其他服務。 換句話說就是:socket單元中的 Service= 所指向的服務單元中的 Sockets= 設定未必要反向指回去。
- 如果多次使用此選項,那麼最終的結果將是多個socket單元的合集。如果將此項設為空,那麼先前設定的socket單元的列表將被清空。
StartLimitInterval=, StartLimitBurst=
- 限制該服務的啟動頻率。預設值是每10秒內不得超過5次(StartLimitInterval=10s StartLimitBurst=5)。
- StartLimitInterval= 的預設值等於systemd配置檔案中 DefaultStartLimitInterval= 的值,"0"表示取消啟動頻率限制。
- StartLimitBurst= 的預設值等於systemd配置檔案中 DefaultStartLimitBurst= 的值。
- 雖然這兩個選項經常與 Restart= 一起使用,但是它們不只限制 Restart= 羅輯所導致的重啟,而是限制所有型別的啟動(包括手動啟動)。 注意,當 Restart=邏輯所導致的重啟超出了啟動頻率限制之後,Restart= 邏輯將會被禁用(也就是不會在下一個時間段內再次嘗試重啟), 然而,如果該單元隨後又被手動重啟,那麼 Restart= 羅輯將被再次啟用。 注意,"systemctl reset-failed ..."命令會清除該服務的重啟次數計數器,這通常用於在手動啟動之前清除啟動限制。
StartLimitAction=
- 設定到達啟動頻率限制後觸發什麼動作。 可設為 none(預設值), reboot, reboot-force, reboot-immediate, poweroff, poweroff-force, poweroff-immediate 之一。
- "none"表示除了禁止再次啟動之外,不觸發任何動作。
- "reboot"表示觸發常規的系統重啟的動作,相當於執行"systemctl reboot"命令。
- "reboot-force"表示觸發系統的強制重啟動作(強制殺死所有程序但不會造成檔案系統不一致),相當於執行"systemctl reboot -f"命令。
- "reboot-immediate"表示立即呼叫核心的reboot(2)函式,可能會造成檔案系統的資料丟失。
- poweroff, poweroff-force, poweroff-immediate 與對應的"reboot*"項含義類似,不同之處僅僅在於是關機而不是重啟。
FailureAction=
- 設定當該服務進入失敗(failed)狀態時所觸發的動作。取值範圍與預設值都與 StartLimitAction= 完全相同。
RebootArgument=
- 設定reboot(2)系統呼叫的可選引數,僅用於 StartLimitAction= 與 FailureAction= 的重啟動作。 其作用與"systemctl reboot [arg]"命令中的可選引數[arg]完全相同。
FileDescriptorStoreMax=
- 允許在 systemd 中最多為該服務儲存多少個使用sd_pid_notify_with_fds(3)的"FDSTORE=1"訊息的檔案描述符,預設值為"0"(不儲存)。 用於實現重啟該服務而不會丟失其狀態(前提是該服務將各種狀態序列化之後儲存在 /run 中,同時將檔案描述符交給 systemd 暫存)。 所有被 systemd 暫存的檔案描述符都將在該服務重啟之後交還給該服務的主程序。 所有被 systemd 暫存的檔案描述符都將在遇到如下兩種情況時被自動關閉: (1)收到 POLLHUP 或 POLLERR 訊號;(2)該服務被徹底停止,並且沒有任何剩餘的任務佇列
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 的前提下,才可以設定多個命令列,且必須用分號(;)將每個命令列隔開(分號自身用"\;"表示)。
例如: ExecStart=/bin/echo one ; /bin/echo "two two" 每個命令列的內部以空格分隔,每一項的邊界都可以用單引號或者雙引號進行界定。 第一項是要執行的命令,隨後的各項則是命令的引數。 行尾的反斜槓(\)將被視作續行符,這和bash的續行語法類似。例如: ExecStart=/bin/echo / >/dev/null & \; \ /bin/ls 的含義是向 /bin/echo 命令傳遞五個引數:"/", ">/dev/null", "&", ";", "/bin/ls". 命令列的語法刻意保持了與shell的相似性,但並不相同。 特別的,重定向(<, <<, >, >)、管道(|)、後臺執行(&),以及其他下文未明確提及的符號都不被支援。 第一項,要執行的命令,必須使用絕對路徑。可以在其中包含空格,但是不可以包含控制字元。 可以在隨後的各項命令引數中使用 systemd.unit(5) 中描述的"%"系列特殊符號,但不可用於命令自身(第一項)。 此外,還可以使用C語言風格的轉義序列(含義也相同),但只能識別如下符號:\a \b \f \n \r \t \v \\ \" \' \s \xxx \nnn 此外,還支援兩種不同的環境變數替換方式("${FOO}"與"$FOO")。
下面的兩個例子,將能清除的體現兩者的差別:
例(1):
Environment="ONE=one" 'TWO=two two' ExecStart=/bin/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", "" 同時給第二個 /bin/echo 依次傳遞如下三個引數: "one", "two two", "too" 具體說來就是: "${FOO}"的內容將原封不動的轉化為一個單獨的命令列引數,無論其中是否包含空格與引號,也無論它是否為空。 "$FOO"的內容將將原封不動的插入命令列中,但對插入內容的解釋卻遵守一般的命令列解析規則。 此外,如果想要傳遞美元符號($)自身,則必須使用"$$"。而那些無法在替換時確定內容的變數將被當做空字串。 注意,不可以在第一項(也就是命令的絕對路徑)中使用變數替換。 這裡使用的變數必須首先在 Environment= 或 EnvironmentFile= 中定義。 此外,在systemd.exec(5)手冊的"派生程序中的環境變數"小節中列出的"靜態變數"也可以使用。 例如,$USER 就是一個"靜態變數",但是,$TERM 則不是"靜態變數"。 注意,這裡的命令列並不直接支援shell命令,但是可以通過模仿下面這個變通的方法來實現: ExecStart=/bin/sh -c 'dmesg | tac'
例1. 簡單服務
下面的單元檔案建立了一個執行 /usr/sbin/foo-daemon 守護程序的服務。未設定的 Type= 等價於預設的 Type=simple 執行 /usr/sbin/foo-daemon 程序之後,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=Cleanup old Foo data
[Service]
Type=oneshot ExecStart=/usr/sbin/foo-cleanup
[Install]
WantedBy=multi-user.target
注意,在 /usr/sbin/foo-cleanup 執行結束前,該服務一直處於'正在啟動中'的狀態,而一旦執行結束, 該服務又立即變為'停止'狀態,也就是說,對於 Type=oneshot 型別的服務,不存在'活動'狀態。 這意味著,如果再一次啟動該服務,將會再一次執行該服務定義的動作。 注意,在時間順序上晚於該服務的單元,將會一直等到該服務變成'停止'狀態後,才會開始啟動。
Type=oneshot 是唯一可以設定多個 ExecStart= 的服務型別。多個 ExecStart= 指令將按照它們出現的順序依次執行, 一旦遇到錯誤,就會立即停止,不再繼續執行,同時該服務也將進入'失敗'狀態。
例3. 可停止的一次性服務
有時候,單元需要執行一個程式以完成某個設定(啟動),然後又需要再執行另一個程式以撤消先前的設定(停止), 而在設定持續有效的時段中,該單元應該視為處於'活動'狀態,但實際上並無任何程式在持續執行。 網路配置服務就是一個典型的例子。此外,只能啟動一次(不可多次啟動)的一次性服務,也是一個例子。
可以通過設定 RemainAfterExit=yes 來滿足這種需求。 在這種情況下,systemd 將會在啟動成功後將該單元視為處於'活動'狀態(而不是'停止'狀態)。 RemainAfterExit=yes 雖然可以用於所有 Type= 型別,但是主要用於 Type=oneshot 和 Type=simple 型別。 對於 Type=oneshot 型別,systemd 一直等到服務啟動成功之後,才會將該服務置於'活動'狀態。 所以,依賴於該服務的其他單元必須等待該服務啟動成功之後,才能啟動。 但是對於 Type=simple 型別,依賴於該服務的其他單元無需等待,將會和該服務同時並行啟動。
下面的類似展示了一個簡單的靜態防火牆服務(simple-firewall.service):
[Unit]
Description=Simple firewall
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/simple-firewall-start
ExecStop=/usr/local/sbin/simple-firewall-stop
[Install]
WantedBy=multi-user.target
因為服務啟動成功後一直處於'活動'狀態,所以再次執行"systemctl start simple-firewall.service"命令不會有任何效果。
例4. 傳統的服務
多數傳統的守護程序(服務)在啟動時會轉入後臺執行。systemd 通過 Type=forking 來支援這種工作方式。 對於 Type=forking 型別的服務,如果最初啟動的程序尚未退出,那麼該單元將依然處於'正在初始化中'狀態。 當最初的程序成功退出,並且至少有一個程序仍然在執行(並且 RemainAfterExit=no),該服務才被視為處於'活動'狀態。
對於單程序的傳統服務,當最初的程序成功退出後,將會只剩單獨一個程序仍然在持續執行, systemd 將會把這個唯一剩餘的程序視為該服務的主程序。 僅在這種情況下,才將可以在 ExecReload=, ExecStop= ... 之類的選項中使用 $MAINPID 變數。 對於多程序的傳統服務,當最初的程序成功退出後,將會剩餘多個程序在持續執行, 因此,systemd 無法確定哪一個程序才是該服務的主程序。在這種情況下,不可以使用 $MAINPID 變數。
然而,如果主程序會建立傳統的PID檔案,那麼應該將 PIDFile= 設為此PID檔案的絕對路徑, 以幫助 systemd 從該PID檔案中讀取主程序的PID,從而幫助確定該服務的主程序。 注意,守護程序必須在完成初始化之前寫入PID檔案,否則可能會導致 systemd 讀取失敗(讀取時檔案不存在)。
下面是一個單程序傳統服務的示例:
[Unit]
Description=Some simple daemon
[Service]
Type=forking
ExecStart=/usr/sbin/my-simple-daemon -d
[Install]
WantedBy=multi-user.target
參見 systemd.kill(5) 以瞭解如何結束服務程序。
例5. D-Bus 服務
對於需要在 D-Bus 系統總線上註冊一個名字的服務,應該使用 Type=dbus 並且設定相應的 BusName= 值。 該服務不可以派生任何子程序。一旦從 D-Bus 系統匯流排成功獲取所需的名字,該服務即被視為初始化成功。
下面是一個典型的 D-Bus 服務:
[Unit]
Description=Simple DBus service
[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
參見system-kill手冊以瞭解如何結束服務。
例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 將會認為該服務一直處於'正在啟動中',並在超時後將其殺死。 關於如何支援該通知協議,參見 sd_notify(3) 手冊頁。 參見 systemd.kill(5) 手冊以瞭解如何結束服務。