守護進程和inetd超級服務器

13.1 概述

守護進程(daemon)是在後臺運行且不與任何控制終端關聯的進程。它是一個生存期較長的進程,通常獨立於控制終端並且週期性地執行某種任務或等待處理某些發生的事件。守護進程常常在系統引導裝入時啓動,在系統關閉時終止。

守護進程有多種啓動的方法:
1、在系統啓動階段,許多守護進程由系統初始化腳本啓動。
2、許多網絡服務器將由inetd超級服務器啓動。inetd一般是由系統初始化腳本啓動。
3、cron守護進程按照規則定期執行一些程序,而由他氣動執行的程序同樣作爲守護進程運行。cron也是由方法1啓動。
4、at命令用於指定將來某個時刻的程序執行。這些程序的執行時刻的到來,通常由cron守護進程啓動執行它們,因此這些程序同樣作爲守護進程運行。
5、守護進程還可以從用戶終端或在前臺或在後臺啓動。
守護進程沒有控制終端,所以當有事發生時它們得有輸出消息的某種方法可用。syslog函數是輸出這些消息的標準方法,它把這些消息發送給syslogd守護進程。

13.2 syslogd守護進程

syslogd守護進程通常由某個系統初始化腳本啓動,而且在系統工作期間一直運行。syslogd實現在啓動時執行以下步驟:
1、讀取配置文件。通常爲/etc/syslog.conf的配置文件指定本守護進程可能收取的各種日誌消息應該如何處理。
2、創建一個unix域數據包套接字,給他捆綁路徑名/var/run/log(某些系統上是/dev/log)。
3、創建一個UDP套接字,給他捆綁端口514(syslog服務使用的端口號)。
4、打開路徑名/dev/klog。來自內核中的任何消息都看做是這個設備的輸入。

13.3 syslog函數

從守護進程中登記消息的常用技巧是調用syslog函數。
  1. #include <syslog.h>
  2. void syslog(int priority, const char *message, ...);
第一個參數priority是級別(level)和設施(facility)兩者的結合。
level是日誌消息的等級,從0到7。
LOG_EMERG    0    系統不可用(最高優先級)
LOG_ALERT    1    必須立即採取行動
LOG_CRIT     2    臨界條件
LOG_ERR      3    出錯條件
LOG_WARNING  4    警告條件
LOG_NOTICE   5    正常而重要的條件(默認值)
LOG_INFO     6    通告消息
LOG_DEBUG    7    調試級消息(最低優先級)

facility是用於標識消息發送進程的類型。
LOG_AUTH        安全/授權消息
LOG_AUTHPRIV    安全/授權消息(私用)
LOG_CRON        cron守護進程
LOG_DAEMON      系統守護進程
LOG_FTP         FTP守護進程
LOG_KERN        內核消息
LOG_LOCAL0      本地使用(LOG_LOCAL0 ~ LOG_LOCAL7)
LOG_LOCAL7      本地使用
LOG_SYSLOG      由syslogd內部產生的消息
LOG_USER        用戶級消息(默認值)
LOG_UUCP        UUCP系統

  1. #include <syslog.h>
  2. void openlog(const char *ident, int options, int facility);
  3. void closelog(void);
openlog可以在首次條用syslog前調用,closelog可以在應用程序不在需要發送日誌消息時調用。
ident參數是一個syslog冠於每個日誌消息之前的字符串。通常爲程序名。
options參數是下表中的一個或多個值得邏輯或構成
LOG_CONS        若無法登記到syslogd守護進程則登記到控制檯
LOG_NDELAY      不延遲打開,立即創建套接字
LOG_PERROR      既發送到syslogd守護進程,又登記到標準錯誤輸出
LOG_PID         隨每個日誌消息登記進程ID

13.4 創建守護進程

1  創建子進程,父進程退出

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

2  在子進程中創建新會話

   Linux是一個多用戶多任務系統,每個進程都有一個進程ID,同時每個進程還都屬於某一個進程組,而每個進程組都有一個組長進程,組長進程的標識ID等於進程組的ID,且該進程組ID不會因組長進程的退出而受到影響。會話期是一個或多個進程組的集合,通常,一個會話開始與用戶登錄,終止於用戶退出,在此期間該用戶運行的所有進程都屬於這個會話期。一個會話期可以有一個單獨的控制終端,只有其前臺進程纔可以擁有控制終端,實現與用戶的交互。從shell中啓動的每一個進程將繼承一個與之相結合的終端,以便進程與用戶交互,但是守護進程不需要這些,子進程繼承父進程的會話期和進程組ID,子進程會受到發送給該會話期的信號的影響,所以守護進程應該創建一個新的會話期,這個步驟是創建守護進程中最重要的一步,雖然它的實現非常簡單,但它的意義卻非常重大。在這裏使用的是系統函數setsid來實現的。 

   setsid函數用於創建一個新的會話,並擔任該會話組的組長。調用setsid有下面的3個作用:
     讓進程擺脫原會話的控制
     讓進程擺脫原進程組的控制
     讓進程擺脫原控制終端的控制

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

3  改變當前目錄爲根目錄

   使用fork創建的子進程繼承了父進程的當前工作目錄。守護進程不應當使用父進程的工作目錄,應該設置自己的工作目錄,通常可以通過chdir()來完成,一般可以將其設置爲根目錄,不過有些守護進程需要將它設置到自己特定的工作目錄,但此時必須保證所設置的工作目錄處於一個不能卸載的文件系統中,因爲守護進程通常在系統引導後是一直存在的。

4  重設文件權限掩碼

   守護進程從父進程繼承來的文件創建方式掩碼可能會拒絕設置某些許可權限,文件權限掩碼是指屏蔽掉文件權限中的對應位。比如,有個文件權限掩碼是050,它就屏蔽了文件組擁有者的可讀與可執行權限。由於使用fork函數新建的子進程繼承了父進程的文件權限掩碼,可能使守護進程的執行出現問題,因此,把文件權限掩碼設置爲0,可以大大增強該守護進程的靈活性。設置文件權限掩碼的函數是umask。在這裏,通常的使用方法爲umask(0)。

5  關閉文件描述符

  一般情況下,進程啓動時都會自動打開終端文件,但是守護進程已經與終端脫離,所以終端描述符應該關閉。用fork函數新建的子進程也會從父進程那裏繼承一些已經打開了的文件。這些被打開的文件可能永遠不會被守護進程讀寫,但它們一樣消耗系統資源,而且可能導致所在的文件系統無法卸下。

6  忽略SIGCHLD信號

   這一步只對需要創建子進程的守護進程纔有必要,很多服務器守護進程設計成通過派生子進程來處理客戶端的請求,如果父進程不對SIGCHLD信號進行處理的話,子進程在終止後變成殭屍進程,通過將信號SIGCHLD的處理方式設置爲SIG_IGN可以避免這種情況發生。

7  用日誌系統記錄出錯信息

   因爲守護進程沒有控制終端,當進程出現錯誤時無法寫入到標準輸出上,可以通過調用syslog將出錯信息寫入到指定的文件中。

8  守護進程退出處理

   當用戶需要外部停止守護進程運行時,往往會使用 kill命令停止該守護進程。所以,守護進程中需要編碼來實現kill發出的signal信號處理,達到進程的正常退出。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章