Linux守護進程
一、 守護進程概述
守護進程,也就是通常所說的Daemon進程,是Linux中的后臺服務進程。它是一個生存期較長的進程,通常獨立于控制終端并且周期性的執行某種任務或等待處理某些發生的事件。守護進程常常在系統啟動時開始運行,在系統關閉時終止。
Linux系統有很多守護進程,大多數服務都是用守護進程實現的。例如常見的常見的守護進程包括系統日志進程syslogd、 web服務器httpd、郵件服務器sendmail和數據庫服務器mysqld等。
二、進程與終端
在Linux中,每一個系統與用戶進行交流的界面稱為終端。從該終端開始運行的進程都會依附于這個終端,這個終端稱為這些進程的控制終端。當控制終端被關閉時,相應的進程都會被自動關閉。
守護進程能夠突破這種限制,它從開始運行,直到整個系統關閉才會退出。如果想讓某個進程不會因為用戶或終端的變化而受到影響,就必須把這個進程變成一個守護進程。
三、 查看守護進程
命令: ps axj
父進程ID : PPID
進程ID : PID
進程組ID : PGID
會話期ID : SID
終端ID : TTY
終端進程組ID : TPGID
狀態 : STAT
用戶 : UID
運行時間 : TIME
指令: COMMAND
四、 Linux守護進程編寫(五步)
1. 創建子進程,父進程退出
第一步完成以后,子進程就在形式上做到了與控制終端的脫離
由于父進程已經先于子進程退出,子進程變成孤兒進程
pid = fork();
if (pid > 0) /*父進程退出*/
{
exit(0);
}
由于守護進程是脫離控制終端的,因此,完成第一步后就會在shell終端里造成一程序已經運行完畢的假象。之后的所有后續工作都在子進程中完成,而用戶在shell終端里則可以執行其他的命令,從而在形式上做到了與控制終端的脫離
由于父進程已經先于子進程退出,會造成子進程沒有父進程,從而變成一個孤兒進程。在Linux中,每當系統發現一個孤兒進程,就會自動由1號進程收養。原先的子進程就會變成init進程的子進程。
2. 在子進程中創建新會話
setsid()函數作用
setsid函數用于創建一個新的會話,并使得當前進程成為新會話組的組長setsid函數能夠使進程完全獨立出來,從而脫離所有其他進程的控制。
進程屬于一個進程組,進程組號(GID)就是進程組長的進程號(PID)。登錄會話可以包含多個進程組。這些進程組共享一個控制終端。這個控制終端通常是創建進程的登錄終端。控制終端,登錄會話和進程組通常是從父進程繼承下來的。我們的目的就是要擺脫它們,使之不受它們的影響。方法是在第1點的基礎上,調用setsid()使進程成為會話組長。
進程組
一個或多個進程的集合。進程組由進程組ID來唯一標識。每個進程組都有一個組長進程,進程組ID就是組長進程的進程號。
會話期
會話組是一個或多個進程組的集合
進程組 對話期 終端
3. 修改當前工作目錄
chdir(“/tmp”);
通常的做法是讓“/”或”/tmp”作為守護進程的當前工作目錄 。
在進程運行過程中,當前目錄所在的文件系統是不能卸載的。
chdir函數可以改變進程當前工作目錄
4. 重設置文件權限掩碼
umask(0);
文件權限掩碼是指文件權限中被屏蔽掉的對應位。把文件權限掩碼設置為0,可以增加該守護進程的靈活性。設置文件權限掩碼的函數umask();
5. 關閉文件描述符
fdtablesize = getdtablesize();
for (fd = 0; fd < fdtablesize; fd++)
close(fd);
新建的子進程會從父進程那里繼承所有已經打開的文件。在創建完新的會話后,守護進程已經脫離任何控制終端,應當關閉用不到的文件。這些被打開的文件可能永遠不會被守護進程讀或寫,但它們一樣消耗系統資源,而且可能導致所在的文件系統無法卸載
從終端輸入的字符不可能達到守護進程,守護進程中用常規的方法(如printf)輸出的字符也不可能在終端上顯示出來。所以,文件描述符為0、1和2的三個文件(對應標準輸入、標準輸出和標準錯誤這三個流)已經失去了存在的意義,也應被關閉。
五、 應用代碼舉例
創建一個守護進程每隔1s向daemon.txt文件中寫入一次系統時間。
#include
#include
#include
#include
#include
#include
#include
int main()
{
pid_t pid;
int num,i;
int fd;
time_t t;
char buf[100];
pid = fork();
//第一步:創建子進程,父進程結束(子進程有init進程托管);
if(pid == 0)
{
//第二步:創建新會話,脫離原來的進程,脫離控制終端,脫離原來的進程組;
setsid();
//第三步:修改當前目錄,每一個進程都有一個當前目錄;(不是必須的)
chdir("/tmp");
//第四步:重新設置文件權限掩碼(不是必須的)
umask(0);
//第五步:關閉打開的文件描述符(如果父進程打開的文件,子進程會繼承過來)
num = getdtablesize(); //獲得當前打開的文件描述符表
for(i = 0; i < num; i++)
{
close(i);
}
fd = open("daemon.txt",O_WRONLY | O_CREAT ,0666);
while(1)
{
t = time(0);
sprintf(buf,"time: %s\n",ctime(&t));
write(fd,buf,strlen(buf));
sleep(1);
}
}
else if(pid > 0)
{
exit(0);
}
return 0;
}