守護進程的特點 守護進程(Daemon)是在後臺運行的一種特殊進程,它脫離於終端,從而這可避免進程被任何終端所產生的信號打斷,它在執行進程中的產生信息也不在任何終端上顯示。守護進程周期性地執行某種任務或等待處理某些發生的事件,Linux的大多數伺服器就是用守護進程實現的。 守護進程編程要點 1.屏蔽 ...
首先說一下後臺進程與守護進程的區別
最大的區別有以下幾點:
(a)守護進程已經完全脫離終端控制台了,而後臺程式並未完全脫離終端(在終端未關閉前還是會往終端輸出結果);
(b)守護進程在關閉終端控制台時不會受影響,而後臺程式會隨用戶退出而停止,需要在以nohup command & 格式運行才能避免影響;
(c)守護進程的會話組和當前目錄,文件描述符都是獨立的。後臺運行只是終端進行了一次fork,讓程式在後臺執行,這些都沒改變;
守護進程的特點
守護進程(Daemon)是在後臺運行的一種特殊進程,它脫離於終端,從而這可避免進程被任何終端所產生的信號打斷,它在執行進程中的產生信息也不在任何終端上顯示。守護進程周期性地執行某種任務或等待處理某些發生的事件,Linux的大多數伺服器就是用守護進程實現的。
守護進程編程要點
1.屏蔽一些有關控制終端操作的信號,是為了防止在守護進程沒有正常啟動起來前,控制終端受到干擾退出或掛起。代碼如下:
/* 處理可能的終端信號 */
signal(SIGTTOU, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
signal(SIGHUP , SIG_IGN);
2.在後臺運行。
/* 是父進程,結束父進程,子進程繼續 */
if(fork())
exit(0);
3.脫離控制終端和進程組:
(1)一個進程屬於一個進程組,進程組號(PGID)就是進程組長的進程號(PID)
(2)同進程組中的進程共用一個控制終端,這個控制終端預設是創建進程的終端
(3)一個進程關聯的控制終端和進程組通常是從父進程繼承下來的,因此,這個子進程仍然受到父親進程終端的影響,因為終端產生的信號會發送給前臺進程組的所有進程。
基於以上原因,需要讓為個子進程徹底擺脫該終端的影響,需要調用setsid()使子進程成為新的會話組長,代碼如下:
setsid();
setsid()調用成功後,調用此函數的進程成為新的會話組長和新的進程組長,並與原來的進程組脫離關係。由於會話過程對控制終端的獨占性,進程同時與控制終端脫離。
4.禁止進程重新打開控制終端,採用的辦法是再次創建一個子進程,並讓父親進程退出,該子進程不再是會話組長,從而達到目的。代碼如下:
/* 結束第一子進程,第二子進程繼續 */
if(fork())
exit(0);
5.關閉打開的文件描述符。因為進程從創建它的父進程那裡繼承了打開的文件描述符,一般情況下不再需要。如不關閉,將會浪費系統資源。代碼如下:
#define NOFILE 256
for(i=0; i<NOFILE; i++)
close(i);
6.改變當前工作目錄。進程活動時,其工作目錄所在的文件系統不能卸載。因此需要將守護進程的工作目錄改變到合適的目錄。代碼如下:
chdir("/tmp");
7.重設文件創建掩碼。進程從創建它的父進程那裡繼承了文件創建掩碼。它可能修改守護進程所創建的文件的存取許可權。代碼如下:
umask(0);
8.處理SIGCHLD信號(子進程退出信號)。如果不等待子進程結束,子進程將成為僵屍進程從而占用系統內核資源。
/* 將子進程退出信號設為SIG_IGN,讓系統幫助回收進程資源 */
signal(SIGCHLD, SIG_IGN);
整體代碼如下:
#define NOFILE 256
void DaemonMode()
{
int num = 0;
int fd0, fd1, fd2;
/* 屏蔽可能的信號 */
signal(SIGTTOU, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
signal(SIGHUP , SIG_IGN);
if(fork())
exit(0);
setsid();
if(fork())
exit(0);
chdir("/tmp/httpd");
umask(0);
for(; num<NOFILE; num++)
close(num);
/* 將輸入、輸出重定向。因為之前描述符都關閉了,所以新打開值為0、1、2 */
fd0 = open("/dev/null", O_RDWR);
fd1 = dup(0);
fd2 = dup(0);
signal(SIGCHLD, SIG_IGN);
}
補充 setsid() 函數功能:
如果調用進程已經是一個進程組的組長,則此函數返回錯誤。為了杜絕這種情況,通常先調用fork()創建子進程,
然後使其父進程終止,而子進程繼續,在子進程中調用此函數。
如果調用此函數的進程不是一個進程組組長,則此函數會創建一個新會話,調用setsid()函數的進程成為新的會話的領頭進程,
並與其父進程的會話組和進程組脫離。由於會話對控制終端的獨占性,進程同時與控制終端脫離。