標籤:style blog color os io ar for 檔案 div
守護進程,脫離於終端,可避免被任何的終端訊號所打斷,常用於伺服器程式。我們用ssh遠程登入,可能做的事情就是啟動某個程式,如果不是守護進程,在我們退出終端時,進程立刻被殺死了,因為由終端啟動的進程是tty的子進程。可用cron或nohup來啟動。另外的啟動方式有1.在系統啟動時由啟動指令碼啟動,通常放在/etc/rc.d/.2.利用inetd超級伺服器啟動,如telnet等。
守護進程編程要點
1.屏蔽一些有關控制終端的訊號。signal(SIGTTOU,SIG_IGN);
2.讓子進程在後台運行,父進程退出。
if(pid=fork()) exit(0);
3.脫離控制終端和進程組setsid(),調用此函數的進程成為新的交談群組長和新的進程組長,並與原來的進程組脫離關係。
4.禁止進程重新開啟控制終端,進程已經成為無終端的回話組長和新的進程組長,但他可以重新申請開啟一個控制終端,採用的辦法是再次建立一個子進程,並讓父進程退出。
5.關閉開啟的檔案描述符包括標準輸入輸出,以節省資源。
#define NOFILE 256
for(i=0;i<NOFILE;i++) close(i);
6.寫日誌的話需要改變當前工作目錄,如chdir("/tmp");
7.重設檔案建立掩碼umask(0);
8.處理SIGCHLD訊號,對於某些需要在請求到來時讓子進程處理的一種方式是父進程等待子進程結束,但增加父進程的負擔,影響並發效能。另一種簡單方式是將子進程退出的SIGCHLD的操作設為SIG_IGN方式,讓系統協助回收僵死進程資源。signal(SIGCHLD,SIG_IGN)
一下是一個簡單的守護進程樣本:
#include<unistd.h>#include<signal.h>#include<fcntl.h>#include<sys/syslog.h>#include<sys/param.h>//包含NOFILE的宏定義#include<sys/types.h>#include<sys/stat.h>#include<stdio.h>#include<stdlib.h>int init_daemon(const char *pname,int facility){ int pid; int i; signal(SIGTTOU,SIG_IGN);//處理可能的終端訊號 signal(SIGTTIN,SIG_IGN); signal(SIGTSTP,SIG_IGN); signal(SIGHUP,SIG_IGN); if(pid==fork()) exit(0); else if(pid<0){ perror("fork"); exit(-1); } setsid();//設定新交談群組長,新進程組長,脫離終端 if(pid=fork()) exit(0);//子進程不能再申請終端 else if(pid<0){ perror("fork"); exit(-1); } for(i=0;i<NOFILE;i++) close(i); open("/dev/null",O_RDONLY); open("/dev/null",O_RDWR);//描述符0,1,2都定向到null open("/dev/null",O_RDWR); chdir("/tmp");//修改主目錄
umask(0);//重設檔案掩碼
signal(SIGCHLD,SIG_IGN);
openlog(pname,LOG_PID,facility);
//與守護進程建立聯絡,加上進程號,檔案名稱
return ;}int main(int argc,char*argv[]){ FILE* fp; time_t ticks; init_daemon(argv[0],LOG_KERN); while(1){ sleep(1); ticks=time(0);//time(NULL); syslog(LOG_INFO,"%s",(char*)asctime(localtime(&ticks))); }}
在日誌中記錄當時的本地時間,可以通過cat /var/log/syslog |grep 進程id 來查看。
在Linux中專門提供了一個函數來完成這個daemon化的過程,這個函數的原型如下
int daemon (int __nochdir, int __noclose);
如果__nochdir的值為0,則將切換工作目錄為根目錄;如果__noclose為0,則將標準輸入,輸出和標準錯誤都重新導向到/dev /null。
經過這個函數調用後的程式將運行在後台,成為一個daemon程式,而linux下大多的服務都是以此方式啟動並執行。
我們來看一個簡單的例子。例如編寫例子程式test.c
#include <unistd.h>
#include <stdio.h>
int do_sth()
{
return 0;
}
int main()
{
daemon(0,0);
while ( 1 )
{
do_sth();
sleep(1);
}
}
編譯並運行
$ gcc -o test test.c
$ ./test
程式進入了後台,通過ps查看進程情況,可以看到進程的父進程id為1,即init進程
用lsof查看test進程所開啟的檔案,可以看到檔案描述符0,1,2都被重新導向到/dev/null
並且能夠看到,進程的當前工作目錄(cwd)為根目錄/,daemon函數已經幫我們完成了daemon化的過程,接下來我們只需要關注於程式功能 的實現了。
守護進程編程要點