1、 守護進程的概念:
守護進程(Daemon)是一種運行在後臺的一種特殊的進程,它獨立於控制終端並且週期性的執行某種任務或等待處理某些發生的事件。由於在linux中,每個系統與用戶進行交流的界面成爲終端,每一個從此終端開始運行的進程都會依附於這個終端,這個終端被稱爲這些進程的控制終端,當控制終端被關閉的時候,相應的進程都會自動關閉。但是守護進程卻能突破這種限制,它脫離於終端並且在後臺運行,並且它脫離終端的目的是爲了避免進程在運行的過程中的信息在任何終端中顯示並且進程也不會被任何終端所產生的終端信息所打斷。它從被執行的時候開始運轉,知道整個系統關閉才退出(當然可以認爲的殺死相應的守護進程)。如果想讓某個進程不因爲用戶或中斷或其他變化而影響,那麼就必須把這個進程變成一個守護進程。
2、 守護進程實現的步驟:
(1) 創建子進程,父進程退出(使子進程成爲孤兒進程):這是編寫守護進程的第一步,由於守護進程是脫離終端的,因此完成第一步後就會在shell終端裏造成一個程序已經運行完畢的假象。之後的所有工作在子進程中完成,而用戶在shell終端裏則可以執行其他命令,從而在形式上做到了與控制終端脫離。實現的語句如下:if(pid=fork()){exit(0);}是父進程就結束,然後子進程繼續執行。
(2) 在子進程中創建新的會話(脫離控制終端):
這步是創建守護進程中最重要的一步,雖然實現起來很簡單,但是它的意義非常重要,在這裏使用的是系統函數setsid()來創建一個新的會話,並且擔任該會話組的組長。在這裏有兩個概念需要解釋一下,進程組合會話期。
進程組:是一個或多個進程的集合。進程組有進程組ID來唯一標識。除了進程號(PID)之外,進程組ID也是一個進程的必備屬性。每個進程組都有一個組長進程,其組長進程的進程號等於進程組ID。且該進程組ID不會因組長進程的退出而受到影響。
會話週期:會話期是一個或者多個進程的集合。通常一個會話開始於用戶的登錄,終止於用戶的退出,在此期間該用戶運行的所有進程都屬於這個會話期。
Setsid()函數的相關內容:
(1) setsid()函數的作用:創建一個新的會話,並且擔任該會話組的組長。具體作用包括:讓一個進程擺脫原會話的控制,讓進程擺脫原進程的控制,讓進程擺脫原控制終端的控制。
(2) 創建守護進程要調用setsid()函數的原因:由於創建守護進程的第一步是調用fork()函數來創建子進程,再將父進程退出。由於在調用了fork()函數的時候,子進程拷貝了父進程的會話期、進程組、控制終端等資源、雖然父進程退出了,但是會話期、進程組、控制終端等並沒有改變,因此,需要用setsid()韓式來時該子進程完全獨立出來,從而擺脫其他進程的控制。
(3) 改變當前目錄爲根目錄:
使用fork()創建的子進程是繼承了父進程的當前工作目錄,由於在進程運行中,當前目錄所在的文件系統是不能卸載的,這對以後使用會造成很多的麻煩。因此通常的做法是讓“/”作爲守護進程的當前目錄,當然也可以指定其他的別的目錄來作爲守護進程的工作目錄。
(4) 重設文件權限掩碼:
文件權限掩碼是屏蔽掉文件權限中的對應位。由於使用fork()函數新創建的子進程繼承了父進程的文件權限掩碼,這就給該子進程使用文件帶了很多的麻煩(比如父進程中的文件沒有執行文件的權限,然而在子進程中希望執行相應的文件這個時候就會出問題)。因此在子進程中要把文件的權限掩碼設置成爲0,即在此時有最大的權限,這樣可以大大增強該守護進程的靈活性。設置的方法是:umask(0)。
(5) 關閉文件描述符:
同文件權限碼一樣,用fork()函數新建的子進程會從父進程那裏繼承一些已經打開了的文件。這些文件被打開的文件可能永遠不會被守護進程讀寫,如果不進行關閉的話將會浪費系統的資源,造成進程所在的文件系統無法卸下以及引起預料的錯誤。按照如下方法關閉它們:
for(i=0;i 關閉打開的文件描述符close(i);
(6) 守護進程的退出:
上面建立了守護進程,當用戶需要外部停止守護進程運行時,往往需要使用kill命令來停止該守護進程,所以守護進程中需要編碼來實現kill發出的signal信號處理,達到進程的正常退出。實現該過程的函數是signal函數:
signal(SIGTERM, sigterm_handler);
void sigterm_handler(int arg)
{
//進行相應處理的函數
}。功能是:將一個給定的函數和一個特定的信號聯繫起來,即在收到特定的信號的時候執行相應的函數。
3、守護進程實現的一個簡單的例子:
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAXFILE 65535
int main()
{
pid_t pc;
int i,fd,len;
char *buf="this is a dameon \n";
len = strlen(buf);
pc =fork(); //創建一個進程用來做守護進程
if(pc<0)
{
printf("error fork \n");
exit(1);
}
else if(pc>0)
exit(0); //結束父進程
setsid(); //使子進程獨立1.擺脫原會話控制 2.擺脫原進程組的控制 3.擺脫控制中端的控制
chdir("/"); //改變當前工作目錄,這也是爲了擺脫父進程的影響
umask(0); //重設文件權限掩碼
for(i=0;i<MAXFILE;i++) //關閉文件描述符(常說的輸入,輸出,報錯3個文件),
//因爲守護進程要失去了對所屬的控制終端的聯繫,這三個文件要關閉
close(i);
while(1)
{
if((fd=open("/tmp/dameon.txt",O_CREAT|O_WRONLY|O_APPEND,0600))<0)
{
printf("open file err \n");
exit(0);
}
write(fd,buf,len+1);
close(fd);
sleep(10);
}
}
程序執行的效果: