Linux守護程式及Systemd

尹瑞星發表於2021-08-16

當我們啟動一個前臺任務後,命令列視窗退出,應用也就一起退出,無法訪問了。怎麼才能讓它變成系統的守護程式(daemon),成為一種服務(service),一直在那裡執行呢?

守護程式

前臺任務和後臺任務

只要在命令的尾部加上符號&,啟動的程式就會成為"後臺任務"。如果要讓正在執行的"前臺任務"變為"後臺任務",可以先按ctrl + z,然後執行bg命令(讓最近一個暫停的"後臺任務"繼續執行)。

後臺任務有兩個特點:

  • 繼續當前session對話的標準輸出(stdout)和標準錯誤(stderr)。因此,後臺任務的所有輸出仍然會同步地在命令列下顯示。
  • 不再整合當前session的標準輸入(stdin)。你無法像這個任務輸入指令了。如果它試圖讀取標準輸入,就會暫停執行(halt)。

可以看到,"後臺任務"與"前臺任務"的本質區別只有一個:是否繼承標準輸入。所以,執行後臺任務的同時,使用者還可以輸入其他命令。

SIGHUP訊號

變為"後臺任務"後,一個程式是否就成為了守護程式呢?或者說,使用者退出 session 以後,"後臺任務"是否還會繼續執行?Linux系統是這樣設計的。

  1. 使用者退出session,系統向該session發出SIGHUP訊號
  2. session將SIGHUOP訊號傳送給所有子程式
  3. 子程式收到SIGHUP訊號,自動退出

後臺任務是否會收到SIGHUP訊號由Shell的huponexit引數決定

shopt | grep huponexit

大多數Linux系統這個引數是預設關閉的,但也有開啟的,意味著後臺任務會收到SIGHUP訊號從而隨著session的關閉而退出。

disown、nohup、screen命令

  • disown命令可以將指定任務從後臺任務列表(jobs命令返回結果)之中移除,這樣就不會收到SIGHUP訊號了。

    #移出最近一個正在執行的後臺任務
    $ disown
    
    #移出所有正在執行的後臺任務
    $ disown -r
    
    #移出所有後臺任務
    $ disown -a
    
    #不移出後臺任務,但讓他們不會收到SIGHUP訊號
    $ disown -h
    
    #根據jobid,移出指定的後臺任務
    $ disown %2
    

    使用disown命令之後,還有一個問題。那就是,退出 session 以後,如果後臺程式與標準I/O有互動,它還是會掛掉。這是因為"後臺任務"的標準 I/O 繼承自當前 session,disown命令並沒有改變這一點。一旦"後臺任務"讀寫標準 I/O,就會發現它已經不存在了,所以就報錯終止執行。為了解決這個問題,需要對"後臺任務"的標準 I/O 進行重定向。

    $ node server.js > stdout.txt 2> stderr.txt < /dev/null &
    $ disown
    
  • 比disown更方便的命令是nohup,它阻止SIGHUP訊號、關閉標準輸入、重定向標準輸出和標準錯誤到檔案nohup.out。也就是說,nohup命令實際上將子程式與它所在的 session 分離了。

  • 另一種思路是使用 terminal multiplexer (終端複用器:在同一個終端裡面,管理多個session),典型的就是 Screen 命令和 Tmux 命令。它們可以在當前 session 裡面,新建另一個 session。這樣的話,當前 session 一旦結束,不影響其他 session。而且,以後重新登入,還可以再連上早先新建的 session。

Systemd

除了專用工具以外,Linux系統有自己的守護程式管理工具 Systemd 。它是作業系統的一部分,直接與核心互動,效能出色,功能極其強大。我們完全可以將程式交給 Systemd ,讓系統統一管理,成為真正意義上的系統服務。

歷史上,Linux的啟動一直採用init 程式。

img

作業系統接管硬體後,首先讀入/boot目錄下的核心檔案到記憶體裡,然後啟動init程式(pid 1)初始化系統環境。

早期linux守護程式是通過init程式執行的,通過確定執行級別(每個級別對一個一個目錄/etc/runN.d 存放開機啟動的程式)來啟動不同的守護程式,執行級別裡面的程式都是連結檔案,指向另一個目錄/etc/init.d,真正啟動指令碼都統一放在這個目錄中。(init.d表示是一個目錄,用於區分/etc/init程式)。

開機啟動程式載入完畢後,就讓使用者登入了。

由於init程式啟動時間長(序列執行),啟動指令碼複雜,systemd誕生了。

systemd不是一個命令,而是一組命令,有systemctlhostnamectl等。

Systemd可以管理所有系統資源呢,不通資源統稱為Unit。每個unit都有一個配置檔案,告訴systemd怎麼啟動這個unit。Systemd 預設從目錄/etc/systemd/system/讀取配置檔案。但是,裡面存放的大部分檔案都是符號連結,指向目錄/usr/lib/systemd/system/,真正的配置檔案存放在那個目錄。systemctl enable命令用於在上面兩個目錄之間,建立符號連結關係,相當於啟用開機啟動。

一旦修改unit配置檔案,就要執行systemctl daemon-reload重新載入配置檔案。

systemctl cat docker.service可以檢視配置檔案內容,配置檔案有以下幾部分組成:

  • [Unit]:定義Unit後設資料以及與其他Unit的關係

    image-20210816182942511

  • [Service]:service的配置,只有service型別才有這個區塊

    image-20210816183247107

  • [Install]:通常是最後一個區塊,定義如何啟動以及是否開機啟動。

    image-20210816183139587

啟動計算機的時候,需要啟動大量的 Unit。如果每一次啟動,都要一一寫明本次啟動需要哪些 Unit,顯然非常不方便。Systemd 的解決方案就是 Target。啟動某個 Target 的時候,Systemd 就會啟動裡面所有的 Unit。

Systemd 統一管理所有 Unit 的啟動日誌。帶來的好處就是,可以只用journalctl一個命令,檢視所有日誌(核心日誌和應用日誌)。日誌的配置檔案是/etc/systemd/journald.conf

相關文章