13.1 概述
守護進程(daemon)是在後臺運行且不與任何控制終端關聯的進程。它是一個生存期較長的進程,通常獨立於控制終端並且週期性地執行某種任務或等待處理某些發生的事件。守護進程常常在系統引導裝入時啓動,在系統關閉時終止。13.2 syslogd守護進程
13.3 syslog函數
#include <syslog.h>
void syslog(int priority, const char *message, ...);
#include <syslog.h>
void openlog(const char *ident, int options, int facility);
void closelog(void);
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信號處理,達到進程的正常退出。