|
UNIX下的網(wǎng)絡(luò)服務(wù)程序,如Web Server,F(xiàn)TP,Telnet一般都是由守護(hù)進(jìn)程(Daemon)來實(shí)現(xiàn)的。守護(hù)進(jìn)程不占用終端,在后臺(tái)運(yùn)行。UNIX的守護(hù)進(jìn)程一般都命名為 *d 的形式,如httpd,telnetd等等。其實(shí),守護(hù)進(jìn)程的實(shí)現(xiàn)是非常簡(jiǎn)單的,在我的程序中,我使用一個(gè)INIT_DAEMON宏來實(shí)現(xiàn)守護(hù)進(jìn)程的初始化工作,如圖2.4所示。第一次調(diào)用fork函數(shù),為避免掛起控制終端將守護(hù)進(jìn)程放入后臺(tái)執(zhí)行。然后調(diào)用setsid函數(shù)脫離控制終端,登錄會(huì)話和進(jìn)程組,使該進(jìn)程成為會(huì)話組長,與原來的登錄會(huì)話和進(jìn)程組脫離,進(jìn)程同時(shí)與控制終端脫離。進(jìn)程已經(jīng)成為無終端的會(huì)話組長。但它可以重新申請(qǐng)打開一個(gè)控制終端??梢酝ㄟ^使進(jìn)程不再成為會(huì)話組長來禁止進(jìn)程重新打開控制終端,這就需要第二次調(diào)用fork函數(shù),父進(jìn)程(會(huì)話組長)退出,子進(jìn)程繼續(xù)執(zhí)行,并不在擁有打開控制終端的能力。在正在執(zhí)行的進(jìn)程中調(diào)用INIT_DAEMON后 進(jìn)程將成 #define INIT_DAEMON \ { \ if( fork() > 0 ) exit(0); \ setsid(); \ if( fork() > 0 ) exit(0); \ } 為守護(hù)進(jìn)程,脫離控制終端進(jìn)入后臺(tái)執(zhí)行。比如,我們的網(wǎng)絡(luò)服務(wù)程序,可以在完成創(chuàng)建套接口,綁定套接口,設(shè)置套接口為監(jiān)聽模式后,變成守護(hù)進(jìn)程進(jìn)入后臺(tái)執(zhí)行而不占用控制終端,這是網(wǎng)絡(luò)服務(wù)程序的常用模式?!? INIT_DAEMON宏只提供了很簡(jiǎn)單的功能,守護(hù)進(jìn)程一旦脫離了終端,退出就成了問題。我目前使用 PS 查出進(jìn)程ID然后 kill 之,笨得實(shí)在可以。以后進(jìn)一步想出一種更好的守護(hù)進(jìn)程退出機(jī)制,有辦法的朋友,請(qǐng)不吝賜教寫信給我,我會(huì)帖出來的。
方法如下: Linux下守護(hù)進(jìn)程的創(chuàng)建有很多的方法,比如我們可以使用cron,inetd等程序來創(chuàng)建.這里介紹在控制終端上有用戶來啟動(dòng)的守護(hù)程序.這種守護(hù)程序不依賴于任何一個(gè)終端,不會(huì)隨著用戶的退出而結(jié)束.這種程序經(jīng)常用于網(wǎng)絡(luò)程序之中. 將一個(gè)程序變?yōu)槭刈o(hù)程序一般按照下面的步驟. 調(diào)用函數(shù)fork,然后父進(jìn)程推出,這樣子進(jìn)程就變?yōu)榱撕笈_(tái)進(jìn)程了.同時(shí)子進(jìn)程不成為進(jìn)程組的組長(組長可能是父進(jìn)程或者是創(chuàng)建父進(jìn)程的進(jìn)程)為第二步系統(tǒng)調(diào)用setsid做準(zhǔn)備. 調(diào)用setsid創(chuàng)建一個(gè)新的會(huì)議組,進(jìn)程成為一個(gè)新的會(huì)議組的組長.這樣這個(gè)會(huì)議組就沒控制終端了. 添加信號(hào)SIGHUP的處理.為后面的會(huì)議組長退出作出處理.同時(shí)再一次調(diào)用fork.然后父進(jìn)程推出.由于進(jìn)程組長退出時(shí)向所有會(huì)議成員發(fā)出SIGHUP,所以我忽略這個(gè)信號(hào).這樣我們的這個(gè)進(jìn)程就沒有了控制終端了. 調(diào)用函數(shù)chdir將進(jìn)程的工作目錄改到根目錄(/).這樣我們的程序可以不占用某個(gè)可卸載的設(shè)備.防止root卸載設(shè)備時(shí)系統(tǒng)報(bào)告設(shè)備忙. 關(guān)閉所有的文件描述符,同時(shí)重定向3個(gè)標(biāo)準(zhǔn)文件描述符. 下面的程序?qū)?chuàng)建一個(gè)守護(hù)程序,這個(gè)程序重定向了標(biāo)準(zhǔn)輸出,標(biāo)準(zhǔn)輸入,和標(biāo)準(zhǔn)錯(cuò)誤輸出. 每分鐘向標(biāo)準(zhǔn)錯(cuò)誤輸出可用的系統(tǒng)內(nèi)存頁數(shù). int main(int argc,char **argv) { struct sigaction act; int error,in,out; time_t now; int memory; /* 父進(jìn)程退出 */ if(fork()!=0) exit(1); /* 創(chuàng)建一個(gè)新的會(huì)議組 */ if(setsid()<0)exit(1); /* 忽略信號(hào)SIGHUP */ act.sa_handler=SIG_IGN; sigemptyset(&act.sa_mask); act.sa_flags=0; if(sigaction(SIGHUP,&act,NULL)==-1)exit(1); /* 子進(jìn)程退出,孫進(jìn)程沒有控制終端了 */ if(fork()!=0) exit(1); if(chdir("/")==-1)exit(1); /* 標(biāo)準(zhǔn)錯(cuò)誤重定向 */ error=open("/tmp/error",O_WRONLY|O_CREAT,0600); dup2(error,STDERR_FILENO); close(error); /* 標(biāo)準(zhǔn)輸入重定向 */ in=open("/tmp/in",O_RDONLY|O_CREAT,0600); if(dup2(in,STDIN_FILENO)==-1)perror("in"); close(in); /* 標(biāo)準(zhǔn)輸出重定向 */ out=open("/tmp/out",O_WRONLY|O_CREAT,0600); if(dup2(out,STDOUT_FILENO)==-1)perror("out"); close(out); while(1) { time(&now); memory=sysconf(_SC_AVPHYS_PAGES); fprintf(stderr,"時(shí)間\t%s\t\t可用內(nèi)存[%d]\n",ctime(&now),memory); sleep(60); } exit(0); } 運(yùn)行這個(gè)程序后,使用ps -x命令可以看到這個(gè)程序的tty是?.表示這個(gè)程序沒有控制終端.
|