用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。