守護進程也稱為精靈進程(Daemon),是運行在后台的一種特殊的進程。它獨立於控制終端並且周期性的執行某種任務負等待處理某些發生的事件。因為他們沒有控制終端,所以說他們是在后台運行的。
守護進程的特點:
(1)Linux系統啟動時會啟動很多系統服務進程,守護進程沒有控制終端,不能直接和用戶交互。
(2)其他進程都是在用戶登錄或者運行程序時創建,在運行結束或用戶注銷時終止,但守護進程不受用戶登錄注銷的影響,只受開機關機的影響。
守護進程存在的原因:
daemon函數存在的原因是因為控制終端由於某些原因(如斷開終端連接)會發送一些信號的原因。而接受處理這些信號的缺省動作會讓進程退出。這些信號會由於終端敲一些特殊按鍵產生。
守護進程和后台進程的區別:
(1)守護進程是后台進程,但后台進程不一定是守護進程
(2)守護進程運行是與終端無關的,是不能往終端上打消息的。
(3)守護進程的會話和當前目錄,文件描述符都是獨立的。后台運行只是終端進行了一次fork,讓程序在后台執行。
創建守護進程:
創建守護進程最關鍵的一步是調用setsid函數創建了一個新的會話(session),並成為session Leaser.
#include<unistd.h>
pid_t setsid(void);
該函數調用成功時返回新創建的session的id(其實就是當前進程的id),出錯返回-1.注意,調用這個函數之前,當前進程不允許是進程組的Leader,否則該函數返回-1.要保證當前進程不是不是進程組的Leader也很容易,只要先fork再調用setsid就行了。fork創建的子進程和父進程在同一個進程組里面,進程組的Leader必然是該組的第一個進程,所以子進程不可能是改組的第一個進程,在子進程中調用setsid就不會有問題了。
成功調用該函數的結果是:
(1)創建一個新的session,當前進程稱為session Leader,當前進程的id就是session的id。
(2)創建一個新的進程組,當前進程稱為進程租的Leader,當前進程的id就是進程租的id。
(3)如果當前進程原本有一個控制終端,則他失去這個控制終端,稱為一個沒有控制終端的進程。所謂失去進程終端是指,原來的進程終端仍然是可打開的,仍然可以讀寫,但只是一個普通的打開文件而不是控制終端了。
創建守護進程的步驟:
(1)調用umask將文件模式創建屏蔽字設置為0
(2)父進程fork出子進程,然后子進程調用setsid,父進程直接退出(保證了子進程不是一個進程組的進程組長)
(3)調用setsid創建一個新的會話(調用成功會使調用進程成為會話的首進程,並成為一個進程租的組長進程,調用進程沒有控制終端)
(4)將當前工作目錄更改為根目錄
(5)關閉不需要的文件描述符
(6)忽略SIGCHLD信號。
要知道在創建守護進程的時候fork一次和fork兩次的兩者的區別,就要先知道第一次fork和第二次fork都起到什么作用:
(1)調用一次fork的作用:
第一次fork的作用是讓shell認為這條命令已經終止,不用掛在終端輸入上,還有就是為了后面上的setsid服務,因為調用setsid函數的進程不能是組長進程,如果不fork出子進程,則此時的父進程是進程組長,就無法調用setsid。當子進程調用完setsid函數之后,子進程是會話組長也是進程組組長,並且脫離了控制終端,此時,不管控制終端如何操作,新的進程都不會收到一些信號使得進程退出。
(2)第二次fork的作用:
雖然當前關閉了和終端的聯系,但是后期可能會誤操作打開了終端。
只有會話首進程能打開終端設備,也就是在fork一次,再把父進程退出,再次fork的子進程作為守護進程繼續運行,保證了該精靈進程不是對話期的首進程,第二次不是必須的,是可選的,市面上有些開源項目也是fork一次。