1. 程式人生 > >用C語言在Linux系統下建立守護程序(Daemon)

用C語言在Linux系統下建立守護程序(Daemon)

      守護程序(daemon)是指在後臺執行的,沒有控制終端與之相連的程序。它獨立於控制終端,週期性地執行某種任務。Linux的大多數伺服器就是用守護程序的方式實現的。如web伺服器程序http等。守護程序在後臺執行,類似於Windows中的系統服務。

      編寫守護程序程式的要點:

(1)讓程式在後臺執行。方法是呼叫fork()產生一個子程序,然後使父程序退出。

(2)呼叫setsid()建立一個新對話期。控制終端、登入會話和程序組通常是從父程序繼承下來的,守護程序要擺脫它們,不受它們的影響,方法是呼叫setsid()使程序成為一個會話組長。setsid()呼叫成功後,程序成為新的會話組長和程序組長,並與原來的登入會話、程序組和控制終端脫離。

(3)禁止程序重新開啟控制終端。經過以上步驟,程序已經成為一個無終端的會話組長,但是它可以重新申請開啟一個終端。為了避免這種情況發生,可以通過使程序不再是會話組長來實現。再一次通過fork()建立新的子程序,使呼叫fork的程序退出。

(4)關閉不再需要的檔案描述符。子程序從父程序繼承開啟的檔案描述符。如不關閉,將會浪費系統資源,造成程序所在的檔案系統無法卸下以及引起無法預料的錯誤。首先獲得最高檔案描述符值,然後用一個迴圈程式,關閉0到最高檔案描述符值的所有檔案描述符。

(5)將當前目錄更改為根目錄。

(6)子程序從父程序繼承的檔案建立遮蔽字可能會拒絕某些許可權。為防止這一點,使用unmask(0)將遮蔽字清零。

(7)處理SIGCHLD訊號。對於伺服器程序,在請求到來時往往生成子程序處理請求。如果父程序不等待子程序結束,子程序將成為殭屍程序(zombie),從而佔用系統資源。如果父程序等待子程序結束,將增加父程序的負擔,影響伺服器程序的併發效能。在Linux下可以簡單地將SIGCHLD訊號的操作設為SIG_IGN。這樣,子程序結束時不會產生殭屍程序。

      守護程序的例項:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <time.h>
#include <syslog.h>

int init_daemon(void) 
{ 
	int pid; 
	int i; 

	//忽略終端I/O訊號,STOP訊號
	signal(SIGTTOU,SIG_IGN);
	signal(SIGTTIN,SIG_IGN);
	signal(SIGTSTP,SIG_IGN);
	signal(SIGHUP,SIG_IGN);
	
	pid = fork();
	if(pid > 0) {
		exit(0); //結束父程序,使得子程序成為後臺程序
	}
	else if(pid < 0) { 
		return -1;
	}

	//建立一個新的程序組,在這個新的程序組中,子程序成為這個程序組的首程序,以使該程序脫離所有終端
	setsid();

	//再次新建一個子程序,退出父程序,保證該程序不是程序組長,同時讓該程序無法再開啟一個新的終端
	pid=fork();
	if( pid > 0) {
		exit(0);
	}
	else if( pid< 0) {
		return -1;
	}

	//關閉所有從父程序繼承的不再需要的檔案描述符
	for(i=0;i< NOFILE;close(i++));

	//改變工作目錄,使得程序不與任何檔案系統聯絡
	chdir("/");

	//將檔案當時建立遮蔽字設定為0
	umask(0);

	//忽略SIGCHLD訊號
	signal(SIGCHLD,SIG_IGN); 
	
	return 0;
}

int main() 
{ 
	time_t now;
	init_daemon();
	syslog(LOG_USER|LOG_INFO,"TestDaemonProcess! \n");
	while(1) { 
		sleep(8);
		time(&now); 
		syslog(LOG_USER|LOG_INFO,"SystemTime: \t%s\t\t\n",ctime(&now));
	} 
}

      編譯執行上述程式。然後用ps -ef 命令檢視程序狀態,該程序狀態如下:


      從結果可以看出該程序具備守護程序的所有特徵。

      檢視/var/log目錄下,先前並不存在的test.log檔案已經有了。


      用vi開啟該日誌檔案,記錄如下:

      

      最後需要關閉此守護程序。關閉的方法是通過ps -ef命令查詢到該程序的程序號,之後再用kill 命令將其殺死。


      注意:使用syslog函式前需要配置。但需要注意的是,在Centos6.x系統中,系統日誌的配置檔案已經發生了變化。不再是原來的/etc/syslog.conf了,而是/etc/rsyslog.conf。開啟上述檔案,在檔案末尾加入下面一行:

user.*    /var/log/test.log

      然後重啟syslog服務。重啟的命令也修改為:/etc/init.d/rsyslog restart


      實際上,Linux提供了完成上述同樣功能的庫函式:

#include <unistd.h>
int daemon(int nochdir,int noclose);

      其中,nochdir引數用於指定是否改變工作目錄,如果給它傳遞0,則工作目錄將被設定為“/”(根目錄),否則繼續使用當前工作目錄。noclose引數為0時,標準輸入、標準輸出和標準錯誤輸出都被重定向到/dev/null檔案,否則依然使用原來的裝置。該函式成功時返回0,失敗返回-1,並設定errno。