
我想編寫自己的systemd
單元檔案來管理真正長時間運行的命令1(以小時為單位)。在尋找的同時ArchWiki 關於 systemd 的文章,它對選擇啟動類型進行了以下說明:
Type=simple
(預設):systemd 認為該服務立即啟動。該進程不得分叉。如果需要在此服務上訂購其他服務,請勿使用此類型,除非它是套接字已啟動的。
為什麼進程根本不能分叉?它是指以守護程序呼叫過程的方式進行分叉(父分叉,然後退出),還是任何類型的分叉?
1我不需要 tmux/screen,因為我想要一種更優雅的方式來檢查狀態並重新啟動服務,而不需要求助於tmux send-keys
.
答案1
允許服務調用fork
系統調用。 Systemd 不會封鎖它,甚至不會注意到它是否封鎖了。這句話特別指在守護程式開始時進行 fork 的做法,以將守護程式與其父程式隔離。 「進程不得分叉[並在子進程中運行服務時退出父進程]」。
這手冊頁更詳細地解釋了這一點,並且使用的措辭不會導致這種特殊的混亂。
許多用作守護程序的程式都有一種模式(通常是預設模式),當它們啟動時,它們將自己與父進程隔離。守護程式啟動,調用fork()
,然後父進程退出。子進程調用setsid()
,使其在自己的進程組和會話中運行,並運行服務。目的是,如果從 shell 命令列呼叫守護進程,即使終端發生某些情況(例如終端關閉),守護程序也不會從內核或 shell 接收任何信號(在這種情況下 shell 會發送 SIGHUP到它知道的所有進程組)。這也導致服務進程被 init 採用,當它退出時會收割它,避免了殭屍如果守護程序是由不適合它的東西啟動的wait()
(如果守護程序是由 shell 啟動的,則不會發生這種情況)。
當守護程式由 systemd 等監控進程啟動時,分叉會適得其反。如果服務崩潰,監視進程應該重新啟動服務,因此它需要知道服務是否退出,如果服務不是監視進程的直接子進程,那麼這就很困難。監視進程不應該永遠消失,也沒有控制終端,因此不必擔心不需要的訊號或收穫。因此,服務進程沒有理由不成為監視器的子進程,而且有充分的理由這樣做。
答案2
忽略這個 Arch 維基頁面。
它在設置方面有相當大的錯誤Type
。而且這不僅限於它的描述simple
。它所說的forking
也是錯的。
對於這類事情的正確建議已經存在了幾十年,比 systemd 本身存在的時間還要長,並且至少可以追溯到 20 世紀 90 年代初。正如我在https://unix.stackexchange.com/a/476608/5132,在 systemd doco 中有一個 Johnny 最新版本的守護進程建議,它很大程度上重複了 daemontools 用戶、IBM、使用 的人們inittab
,以及……好吧……我幾十年來一直在說的。 (當我在 2001 年寫下這個問題時,這已經是一個經常給出的答案。)
重複:
如果您的程式有某種「守護進程」機制,特別是分叉子進程並退出父進程,把它關掉和不要使用它。感謝 daemontools 等人。在很長一段時間以來這一直是一個要求的地方,許多計劃已經增強了以下能力:沒有此類機制在過去 20 多年中一直存在,而其他機制則一開始就沒有預設為“守護程式”,因此可以在其預設操作模式下使用。
服務管理子系統啟動服務程序已經在守護程式上下文中。這些進程不需要「守護程式」。 (事實上,在許多現代作業系統上,認為程式甚至能來自登入會話上下文的“dæmonize”,這就是“dæmonization”的實際含義。阻撓服務管理器定期使用守護程序完成的一些常規操作(例如,將其標準輸出/錯誤捕獲到日誌中)。
更喜歡Type=simple
,提前打開套接字(服務管理打開伺服器套接字並將它們作為已打開的文件描述符傳遞給服務程序),或Type=notify
.
Type=simple
一旦服務進程啟動,就將服務視為就緒(以便可以啟動/停止在其上訂購的服務),並在客戶端嘗試連接到伺服器以進行早期套接字打開時,使用套接字連接語義來延遲服務客戶端服務,直到伺服器真正準備好為止。Type=notify
具有 systemd 和 Linux 所特有的缺點(以及無法從諸如 shell 生成等短期進程中發揮作用的問題systemd-notify
,以及在特權進程中採用將人類可讀形式解析為機器可讀形式的問題,其中解析器問題有已經發生了過去),但具有提供更精細的控制(從服務程序的角度)的優點,即服務何時實際上被認為已準備好。它還允許對狀態輸出進行一些自訂。
兩種類型的服務程序都可以分叉。它正在分叉然後退出原來的進程那就是問題所在。
(應該指出的是,這對於從 shell 運行程式和從服務管理員執行程式來說都是一個問題,使用者看到程式終止並幾乎立即導致另一個 shell 提示字元。事實上,就在今天有人再次問,關於從shell 運行分叉並退出父級的程序,位於為什麼有時當我在終端機中運行程式時,它不會在終端機中運行?.)
Type=oneshot
在這種特殊情況下,這可能不是您想要的,因為只有當整個服務程序運行完成時,服務才被視為準備就緒。它有它的用途,但聽起來它們並不適用於你。
切勿使用Type=forking
.這應該是絕望中的最後手段,因為幾乎沒有程序實際上講協議。他們在別的東西,這實際上是不是該協定無法與該協定正確互操作,並且實際上未發出就緒訊號。
進一步閱讀
- 喬納森·德博因·波拉德 (2001)。 設計 Unix 守護程式時要避免的錯誤。經常給的答案。
- 喬納森·德博因·波拉德 (2015)。您確實不需要守護程式。真的。。 systemd 恐怖屋。
- 喬納森·德博因·波拉德 (2015)。Unix 守護程式的就緒協定問題。經常給的答案。
- https://unix.stackexchange.com/a/401611/5132