Linux守護進程編程規則

寫這篇blog的原因在於,工作中經常需要寫一些守護進程(daemon)。

而我們創建守護進程的方法,往往是簡單粗暴,不按照守護進程的編程來。

創建守護進程的一般方法是:

pid = fork();
if (pid < 0) {  // fork failed
    printf("fork error\n");
    exit(1);
} else if (pid > 0) { // parent process
    return;
} else { // child process
    // daemon begin to execute
}
上面創建守護進程的方法雖然也能實現大體上的功能,但不夠規範。上述的創建方法,可能會導致不必要的交互作用。

因此,有必要搞清楚標準守護進程的創建方法,也就是守護進程的編程規則。


1.守護進程是什麼?

守護進程(daemon)是運行在後臺的一種特殊進程。它獨立於控制終端並且週期性地執行某種任務或等待處理某些發生的事件。

2.守護進程的特徵?

守護進程最重要的特性是後臺運行。在這一點上DOS下的常駐內存程序TSR與之相似。

其次,守護進程必須與其運行前的環境隔離開來。這些環境包括未關閉的文件描述符,控制終端,會話和進程組,工作目錄以及文件創建掩碼等。

這些環境通常是守護進程從執行它的父進程(特別是shell)中繼承下來的。

最後,守護進程的啓動方式有其特殊之處。它可以在Linux系統啓動時從啓動腳本/etc/rc.d中啓動,可以由作業規劃進程crond啓動,還可以由用戶終端(通常是shell)執行。

總之,除開這些特殊性以外,守護進程與普通進程基本上沒有什麼區別。因此,編寫守護進程實際上是把一個普通進程按照上述的守護進程的特性改造成爲守護進程。

3.守護進程的編程規則?

編寫守護進程需要遵循一些基本規則,以便防止產生不必要的交互作用。

(1)  首先要做的是調用umask,重設文件權限掩碼。

文件權限掩碼是指屏蔽掉文件權限中的對應位。比如,有個文件權限掩碼是050,它就屏蔽了文件組擁有者的可讀與可執行權限。
由於使用fork函數新建的子進程繼承了父進程的文件權限掩碼,這就給該子進程使用文件帶來了諸多的麻煩。
因此,把文件權限掩碼設置爲0,可以大大增強該守護進程的靈活性。
設置文件權限掩碼的函數是umask。在這裏,通常的使用方法爲umask(0)。

(2) 調用fork,然後將父進程退出(exit)。

這是編寫守護進程的第一步。由於守護進程是脫離控制終端的,因此,完成第一步後就會在Shell終端裏造成一程序已經運行完畢的假象。
之後的所有工作都在子進程中完成,而用戶在Shell終端裏則可以執行其他命令,從而在形式上做到了與控制終端的脫離。
在Linux中父進程先於子進程退出會造成子進程成爲孤兒進程,而每當系統發現一個孤兒進程是,就會自動由1號進程(init)收養它。
這樣,原先的子進程就會變成init進程的子進程。

(3) 調用setsid以創建一個新會話。

這個步驟是創建守護進程中最重要的一步,雖然它的實現非常簡單,但它的意義卻非常重大。

在這裏使用的是系統函數setsid,在具體介紹setsid之前,首先要了解兩個概念:進程組和會話期
  進程組:是一個或多個進程的集合。進程組有進程組ID來唯一標識。除了進程號(PID)之外,進程組ID也是一個進程的必備屬性。每個進程組都有一個組長進程,其組長         進程的進程號等於進程組ID。且該進程組ID不會因組長進程的退出而受到影響。
  會話週期:會話期是一個或多個進程組的集合。通常,一個會話開始與用戶登錄,終止於用戶退出,在此期間該用戶運行的所有進程都屬於這個會話期。

接下來就可以具體介紹setsid的相關內容:
  setsid函數作用:
  setsid函數用於創建一個新的會話,並擔任該會話組的組長。調用setsid有下面的3個作用:
  讓進程擺脫原會話的控制
  讓進程擺脫原進程組的控制
  讓進程擺脫原控制終端的控制 

那麼,在創建守護進程時爲什麼要調用setsid函數呢?
由於創建守護進程的第一步調用了fork函數來創建子進程,再將父進程退出。由於在調用了fork函數時,子進程全盤拷貝了父進程的會話期、進程組、控制終端等。
雖然父進程退出了,但會話期、進程組、控制終端等並沒有改變,因此,還還不是真正意義上的獨立開來,而setsid函數能夠使進程完全獨立出來,從而擺脫其他進程的控制。

(4) 將當前工作目錄更改爲根目錄。

從父進程繼承過來的當前工作目錄可能在一個裝配文件系統中。因爲守護進程可能會在系統再引導之前就一直存在,所以如果守護進程的當前工作目錄

在一個裝配文件系統中,那麼該文件系統就不可拆卸。這與裝配文件系統的原意不符。

(5) 關閉不再需要的文件描述符。

fork函數新建的子進程會從父進程那裏繼承一些已經打開了的文件。這些被打開的文件可能永遠不會被守護進程讀寫,但它們一樣消耗系統資源,而且可能導致所在的文件系統無法卸下。 

(6) 某些守護進程打開/dev/null*,使其具有文件描述符0、1和2.

因爲守護進程並不與終端設備相關聯,所以不能再終端設備上顯示其輸出,也無處從交互式用戶那裏接收輸入。


*在類Unix系統中,/dev/null,或稱空設備,是一個特殊的設備文件,它丟棄一切寫入其中的數據(但報告寫入操作成功),讀取它則會立即得到一個EOF。

發佈了33 篇原創文章 · 獲贊 6 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章