一、守護進程的概念:
守護進程就是通常說的daemon進程,是linux后台執行的一種進程,他的特點就是,生存周期長,獨立於控制終端並且周期性的執行某種任務,不會隨着終端的關閉而停止運行,直到接收到停止信息才會結束運行。
二、守護進程的創建:
- 創建子進程,父進程退出。
if (fork()) { exit(0); }
先解釋這個語句的意思:
/* * fork()函數: * 三種返回值: * 0:子進程中返回0 * > 0:在父進程中返回子進程ID * -1 : 出錯返回-1 * 所以通過判斷是不是0,就能判斷是不是子進程,不是子進程的全部退出。 * */
為什么做這一步?
由於守護進程是脫離終端而存在的,所以先給終端一種假死的狀態,之后的工作都在子進程中進行。
父進程退出后,子進程沒有父進程變成了一個孤兒進程,Linux中,每當系統發現一個孤兒進程,就會自動由1號進程(也就是init進程:內核啟動的一號進程)收養它,這樣子進程就變成了init進程的子進程。
2.在子進程中創建新會話:
setsid()
這個步驟是創建守護進程最重要的一步,雖然實現非常簡單,但意義卻非常重大。在這里使用的是系統函數setsid(),在具體介紹setsid()之前,讀者首先要了解兩個概念:進程組和會話期。
進程組。進程組是一個或多個進程的集合。進程組由進程組ID來唯一標識。除了進程號(PID)之外,進程組ID也是一個進程的必備屬性。
每個進程組都有一個組長進程,其組長進程的進程號等於進程組ID,且該進程ID不會因組長進程的退出而受到影響。
會話期。會話組是一個或多個進程組的集合。通常,一個會話開始於用戶登錄,終止於用戶退出,在此期間該用戶運行的所有進程都屬於這個會話期。
接下來就可以具體介紹setsid()的相關內容。
setsid()函數的作用。setsid()函數用於創建一個新的會話組,並擔任該會話組的組長。調用setsid()有以下3個作用:
讓進程擺脫原會話的控制。
讓進程擺脫原進程組的控制。
讓進程擺脫原控制終端的控制。
那么,在創建守護進程時為什么要調用setsid()函數呢?讀者可以回憶一下創建守護進程的第一步, 在那里調用了fork()函數來創建子進程再令父進程退出。由於在調用fork()函數時,子進程全盤復制了父進程的會話期、進程組和控制終端等,雖然父進程退出了,但原先的會話期、進程組和控制終端等並沒有改變,因此,還不是真正意義上的獨立。而setsid()函數能夠使進程完全獨立出來,從而脫離所 有其他進程的控制。
3.改變當前目錄為根目錄。
這一步也是必要的步驟。使用fork()創建的子進程繼承了父進程的當前工作目錄。由於在進程運行過程中,當 前目錄所在的文件系統(如“/mnt/usb”等)是不能卸載的,這對以后的使用會造成諸多的麻煩(如系統由於某種原因要進入單用戶模式)。因此,通常的 做法是讓“/”作為守護進程的當前工作目錄,這樣就可以避免上述問題。當然,如有特殊需要,也可以把當前工作目錄換成其他的路徑,如/tmp。改變工作目 錄的常見函數是chdir()。
4.重設文件權限掩碼。
文件權限掩碼是指屏蔽掉文件權限中的對應位。例如,有一個文件權限掩碼是050,它就屏蔽了文件組擁有者的可讀與 可執行權限。由於使用fork()函數新建的子進程繼承了父進程的文件權限掩碼,這就給該子進程使用文件帶來了諸多的麻煩。因此,把文件權限掩碼設置為 0,可以大大增強該守護進程的靈活性。設置文件權限掩碼的函數是umask()。在這里,通常的使用方法為umask(0)。
5.關閉文件描述符。
同文件權限掩碼一樣,用fork()函數新建的子進程會從父進程那里繼承一些已經打開的文件。這些被打開的文件可能永遠不會被守護進程讀或寫,但它們一樣消耗系統資源,而且可能導致所在的文件系統無法被卸載。
