粉絲不過W
守護進程(Daemon)是運行在後臺的一種特殊進程
它獨立於控制終端並且週期性地執行某種任務或等待處理某些發生的事件
守護進程是一種很有用的進程
Linux的大多數服務器就是用守護進程實現的,比如,htemet服務器inetd,Web服務器httpd等
守護進程及其特性
守護進程最重要的特性是後臺運行,其次,守護進程必須與其運行前的環境隔離開,這些環境包括未關閉的文件描述符、控制終端、會話和進程組、工作目錄以及文件創建掩模等,,這些環境通常是守、護進程從執行它的父進程(特別是shell)中繼承下來的,最後,守護進程的啓動方式有其特殊之處。它可以在“n慨系統啓動時從啓動腳本/etc/rc.d中啓動,可以由作業規劃進程cmnd啓動,還可以由用戶終端(通常是shell)執行。
守護進程的編程要點
在後臺運行
爲避免掛起,控制終端將Daemon放人後臺執行,方法是在進程中調用觸使父進程終止,讓Dae瑚n在子進程中後臺執行
if(pid = fork())
{
exit(0);
}
脫離控制終端,登錄會話和進程組
linux中的進程與控制終端、登錄會話和進程組之間的關係:進程屬於一個進程組,進程組號(GID)就是進程組長的進程號(PID)
登錄會話可以包含多個進程組。這些進程組共享一個控制終端,這個控制終端通常是創建進程的登錄終端
控制終端,登錄會話和進程組通常是從父進程繼承下來的。目的就是要擺脫它們,使之不受它們的影響
setsid():
說明:當進程是會話組長時,setsid()調用失敗, 所以第一點要保證進程不是會話組長。
setsid()調用成功後,進程成爲新的會話組長和新的進程組長,並與原來的登錄會話和進程組脫離。由於會話過程對控制終端的獨佔性,進程同時與控制終端脫離
禁止進程重新打開控制終端
進程已經成爲無終端的會話組長。但它可以重新申請打開一個控制終端。可以通過使進程不再成爲會話組長來禁止進程重新打開控制終端:
if(pid = fork())
{
exit(0); //結束第一子進程,第二子進程繼續(第二子進程不再是會話組長)
}
關閉打開的文件描述符
進程從創建它的父進程那裏繼承了打開的文件描述符。如不關閉,將會浪費系統資源,造成進程所在的文件系統無法卸下以及引起無法預料的錯誤
關閉方法:
for(i = 0; i < NOFILE; i++) //關閉打丌的文件描述符
{
close(i);
}
改變當前工作目錄
進程運行時,其工作目錄所在的文件系統不能卸下,一般需要將工作目錄改變到根目錄。對於需要轉儲核心,寫運行日誌
的進程將工作目錄改變到特定目錄,如/tmp:
chdir("/");
重設文件創建掩模
進程從創建它的父進程那裏繼承了文件創建掩模,可能修改守護進程所創建的文件的存取位,爲防止這一點,將文件創
建掩模清除:
umask(0);
處理SlGCHLD信號
處理SIGCHLD信號並不是必須的,但對於某些進程, 特別是服務器進程往往在請求到來時生成子進程處理請求。如 父進程不等待子進程結束,子進程將成爲殭屍進程(zombie),從而佔用系統資源。如 父進程等待子進程結束,將增加父進程的負擔,影響服務器進程的併發性能。可在 Linux下可以簡單地將 SIGCHLD 信號的操作設爲 SLG_IGN:
signal(SIGCHLD, SIG_IGN) //內核在子進程結束時不會產生殭屍進程
守護進程實例
主程序每隔一分鐘向/tmp目錄中的日誌test.log 報告運行狀態。初始化程序中的init_daemon函數負責生成守護進程。可利用init_daemon函數生成自己的守護進程
#include <unistd.h>
#include <signal.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <time.h>
void init_daemon(void);
int main()
{
FILE *fp;
time_t t;
init_daemon(); //初始化Daemon
while(1) //每隔一分鐘向test.log報告運行狀態
{
sleep(60); //睡眠一分鐘
if((fp = fopen("test.log","a")) >= 0)
{
t = time(0);
fprintf(fp, "I'm here at %s \n", asctime(localtime(&t)));
fclose(fp);
}
}
}
void init_daemon(void)
{
int pid;
int i;
if(pid = fork())
{
exit(0); //結束父進程
}
else if(pid < 0)
{
exit(1); //fork失敗,退出
}
setsid(); //第一子進程成爲新的會話組長和進程組長,並與控制終端分離
if(pid = fork())
{
exit(0); //結束第一子進程
}
else if(pid < 0)
{
exit(0); //fork失敗
}
for(i = 0; i < NOFILE; i++) //關閉打開的文件描述符
{
close(i);
}
chdir("/tmp"); //修改工作目錄到/tmp
umask(0); //重建文件創建掩碼
}
查看進程:ps -ef