一江春水向東流

          做一個(gè)有思想的人,期待與每一位熱愛思考的人交流,您的關(guān)注是對(duì)我最大的支持。

            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            44 隨筆 :: 139 文章 :: 81 評(píng)論 :: 0 Trackbacks

          編寫Linux/Unix守護(hù)進(jìn)程 [zz]

          守護(hù)進(jìn)程在Linux/Unix系統(tǒng)中有著廣泛的應(yīng)用。有時(shí),開發(fā)人員也想把自己的程序變成守護(hù)進(jìn)程。在創(chuàng)建一個(gè)守護(hù)進(jìn)程的時(shí)候,要接觸到子進(jìn)程、進(jìn)程組、會(huì)晤期、信號(hào)機(jī)制、文件、目錄和控制終端等多個(gè)概念。因此守護(hù)進(jìn)程還是比較復(fù)雜的,在這里詳細(xì)地討論Linux/Unix的守護(hù)進(jìn)程的編寫,總結(jié)出八條經(jīng)驗(yàn),并給出應(yīng)用范例。

          ?編程要點(diǎn)

          ????1.屏蔽一些有關(guān)控制終端操作的信號(hào)。防止在守護(hù)進(jìn)程沒有正常運(yùn)轉(zhuǎn)起來時(shí),控制終端受到干擾退出或掛起。示例如下:

          signal(SIGTTOU,SIG_IGN); 
          signal(SIGTTIN,SIG_IGN); 
          signal(SIGTSTP,SIG_IGN); 
          signal(SIGHUP ,SIG_IGN);/PRE>
          BR>
          ????所有的信號(hào)都有自己的名字。這些名字都以“SIG”開頭,只是后面有所不同。開發(fā)人員可以通過這些名字了解到系統(tǒng)中發(fā)生了什么事。當(dāng)信號(hào)出現(xiàn)時(shí),開發(fā)人員可以要求系統(tǒng)進(jìn)行以下三種操作:
          ????◆ 忽略信號(hào)。大多數(shù)信號(hào)都是采取這種方式進(jìn)行處理的,這里就采用了這種用法。但值得注意的是對(duì)SIGKILL和SIGSTOP信號(hào)不能做忽略處理。
          ????◆ 捕捉信號(hào)。最常見的情況就是,如果捕捉到SIGCHID信號(hào),則表示子進(jìn)程已經(jīng)終止。然后可在此信號(hào)的捕捉函數(shù)中調(diào)用waitpid()函數(shù)取得該子進(jìn)程的進(jìn)程ID和它的終止?fàn)顟B(tài)。另外,如果進(jìn)程創(chuàng)建了臨時(shí)文件,那么就要為進(jìn)程終止信號(hào)SIGTERM編寫一個(gè)信號(hào)捕捉函數(shù)來清除這些臨時(shí)文件。
          ????◆ 執(zhí)行系統(tǒng)的默認(rèn)動(dòng)作。對(duì)絕大多數(shù)信號(hào)而言,系統(tǒng)的默認(rèn)動(dòng)作都是終止該進(jìn)程。

          ????對(duì)這些有關(guān)終端的信號(hào),一般采用忽略處理,從而保障了終端免受干擾。

          ????這類信號(hào)分別是,SIGTTOU(表示后臺(tái)進(jìn)程寫控制終端)、SIGTTIN(表示后臺(tái)進(jìn)程讀控制終端)、SIGTSTP(表示終端掛起)和SIGHUP(進(jìn)程組長(zhǎng)退出時(shí)向所有會(huì)議成員發(fā)出的)。

          ????2.將程序進(jìn)入后臺(tái)執(zhí)行。由于守護(hù)進(jìn)程最終脫離控制終端,到后臺(tái)去運(yùn)行。方法是在進(jìn)程中調(diào)用fork使父進(jìn)程終止,讓Daemon在子進(jìn)程中后臺(tái)執(zhí)行。這就是常說的“脫殼”。子進(jìn)程繼續(xù)函數(shù)fork()的定義如下:

          #include <sys/types.h>
          #include <unistd.h>
           pid_t fork(void);/PRE>
          BR>
          ????該函數(shù)是Linux/Unix編程中非常重要的函數(shù)。它被調(diào)用一次,但返回兩次。這兩次返回的區(qū)別是子進(jìn)程的返回值為“0”,而父進(jìn)程的返回值為子進(jìn)程的ID。如果出錯(cuò)則返回“-1”。

          ????3.脫離控制終端、登錄會(huì)話和進(jìn)程組。開發(fā)人員如果要擺脫它們,不受它們的影響,一般使用 setsid() 設(shè)置新會(huì)話的領(lǐng)頭進(jìn)程,并與原來的登錄會(huì)話和進(jìn)程組脫離。這只是其中的一種方法,也有如下處理的辦法:

          if  ((fd = open("/dev/tty",O_RDWR)) >= 0) { 
          ioctl(fd,TIOCNOTTY,NULL); 
          close(fd); 
          }/PRE>
          BR>
          ????其中/dev/tty是一個(gè)流設(shè)備,也是終端映射,調(diào)用close()函數(shù)將終端關(guān)閉。

          ????4.禁止進(jìn)程重新打開控制終端。進(jìn)程已經(jīng)成為無終端的會(huì)話組長(zhǎng),但它可以重新申請(qǐng)打開一個(gè)控制終端。開發(fā)人員可以通過不再讓進(jìn)程成為會(huì)話組長(zhǎng)的方式來禁止進(jìn)程重新打開控制終端,需要再次調(diào)用fork函數(shù)。
          ????上面的程序代碼表示結(jié)束第一子進(jìn)程,第二子進(jìn)程繼續(xù)(第二子進(jìn)程不再是會(huì)話組長(zhǎng))。

          ????5. 關(guān)閉打開的文件描述符,并重定向標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出的文件描述符。進(jìn)程從創(chuàng)建它的父進(jìn)程那里繼承了打開的文件描述符。如果不關(guān)閉,將會(huì)浪費(fèi)系統(tǒng)資源,引起無法預(yù)料的錯(cuò)誤。關(guān)閉三者的代碼如下:

          for (fd = 0, fdtablesize = getdtablesize(); 
           fd < fdtablesize; fd++) 
            close(fd);/PRE>
          BR>
          ????但標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出的重定向是可選的。也許有的程序想保留標(biāo)準(zhǔn)輸入(0)、標(biāo)準(zhǔn)輸出(1)和標(biāo)準(zhǔn)錯(cuò)誤輸出(2),那么循環(huán)應(yīng)繞過這三者。代碼如下:

          for (fd =3, fdtablesize = getdtablesize();
          fd < fdtablesize; fd++) 
            close(fd);/PRE>
          BR>
          ????有的程序有些特殊的需求,還需要將這三者重新定向。示例如下:

          error=open("/tmp/error",O_WRONLY|O_CREAT,
          0600);
            dup2(error,2);
           close(error);
           in=open("/tmp/in",O_RDONLY|O_CREAT,0600);
           if(dup2(in,0)==-1)  perror("in");
           close(in);
          out=open("/tmp/out",O_WRONLY|O_CREAT,0600);
           if(dup2(out,1)==-1) perror("out");
           close(out);/PRE>
          BR>
          ????6.改變工作目錄到根目錄或特定目錄進(jìn)程活動(dòng)時(shí),其工作目錄所在的文件系統(tǒng)不能卸下。

          ????一般需要將工作目錄改變到根目錄或特定目錄,注意用戶對(duì)此目錄需要有讀寫權(quán)。防止超級(jí)用戶卸載設(shè)備時(shí)系統(tǒng)報(bào)告設(shè)備忙。

          ????7.處理SIGCHLD信號(hào)。SIGCHLD信號(hào)是子進(jìn)程結(jié)束時(shí),向內(nèi)核發(fā)送的信號(hào)。

          如果父進(jìn)程不等待子進(jìn)程結(jié)束,子進(jìn)程將成為僵尸進(jìn)程(zombie)從而占用系統(tǒng)資源。因此需要對(duì)SIGCHLD信號(hào)做出處理,回收僵尸進(jìn)程的資源,避免造成不必要的資源浪費(fèi)。可以用如下語句:
          ????signal(SIGCHLD,(void *)reap_status);

          ????捕捉信號(hào)SIGCHLD,用下面的函數(shù)進(jìn)行處理:

          void reap_status() 
           { int pid; 
             union wait status; 
             while ((pid = wait3(&status,WNOHANG,NULL)) > 0) 
            …… }/PRE>
          BR>
          ????8.在Linux/Unix下有個(gè)syslogd的守護(hù)進(jìn)程,向用戶提供了syslog()系統(tǒng)調(diào)用。任何程序都可以通過syslog記錄事件。

          ????由于syslog非常好用和易配置,所以很多程序都使用syslog來發(fā)送它們的記錄信息。一般守護(hù)進(jìn)程也使用syslog向系統(tǒng)輸出信息。syslog有三個(gè)函數(shù),一般只需要用syslog(...)函數(shù),openlog()/closelog()可有可無。syslog()在shslog.h定義如下:

          #include <syslog.h>
          void syslog(int priority,char *format,...);/PRE>
          BR>
          ????其中參數(shù)priority指明了進(jìn)程要寫入信息的等級(jí)和用途。第二個(gè)參數(shù)是一個(gè)格式串,指定了記錄輸出的格式。在這個(gè)串的最后需要指定一個(gè)%m,對(duì)應(yīng)errno錯(cuò)誤碼。

          ????應(yīng)用范例

          ????下面給出Linux下編程的守護(hù)進(jìn)程的應(yīng)用范例,在UNIX中,不同版本實(shí)現(xiàn)的細(xì)節(jié)可能不一致,但其實(shí)現(xiàn)的原則是與Linux一致的。

          #include <stdio.h> 
          #include <signal.h> 
          #include <sys/file.h> 
          main(int argc,char **argv)
          {
            time_t now;
            int childpid,fd,fdtablesize;
            int error,in,out;
            /* 忽略終端 I/O信號(hào),STOP信號(hào) */
           signal(SIGTTOU,SIG_IGN);
           signal(SIGTTIN,SIG_IGN);
            signal(SIGTSTP,SIG_IGN); 
            signal(SIGHUP ,SIG_IGN);
            /* 父進(jìn)程退出,程序進(jìn)入后臺(tái)運(yùn)行 */
            if(fork()!=0) exit(1);
             if(setsid()<0)exit(1);/* 創(chuàng)建一個(gè)新的會(huì)議組 */ 
            /* 子進(jìn)程退出,孫進(jìn)程沒有控制終端了 */  
            if(fork()!=0) exit(1);
            if(chdir("/tmp")==-1)exit(1);
          /* 關(guān)閉打開的文件描述符,包括標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出 */ 
           for (fd = 0, fdtablesize = getdtablesize(); fd < fdtablesize; fd++) 
             close(fd);
             umask(0);/*重設(shè)文件創(chuàng)建掩模 */ 
             signal(SIGCHLD,SIG_IGN);/* 忽略SIGCHLD信號(hào) */ 
          /*打開log系統(tǒng)*/
            syslog(LOG_USER|LOG_INFO,"守護(hù)進(jìn)程測(cè)試!n");  
             while(1)  
             {  
              time(&now);
             syslog(LOG_USER|LOG_INFO,"當(dāng)前時(shí)間:t%sttn",ctime(&now));
              sleep(6);
               }  
           }/PRE>
          BR>
          ????此程序在Turbo Linux 4.0下編譯通過。這個(gè)程序比較簡(jiǎn)單,但基本體現(xiàn)了守護(hù)進(jìn)程的編程要點(diǎn)。讀者針對(duì)實(shí)際應(yīng)用中不同的需要,還可以做相應(yīng)的調(diào)整。
          posted on 2007-04-13 14:04 allic 閱讀(994) 評(píng)論(0)  編輯  收藏 所屬分類: linux/UNIX 應(yīng)用開發(fā)
          主站蜘蛛池模板: 库尔勒市| 西青区| 台中市| 五河县| 邢台县| 大宁县| 宁津县| 商城县| 化德县| 乌什县| 利津县| 靖西县| 内丘县| 五寨县| 温州市| 钦州市| 兴和县| 永康市| 榆林市| 深泽县| 克拉玛依市| 文登市| 信宜市| 崇仁县| 鹤壁市| 方正县| 大悟县| 怀宁县| 平利县| 曲靖市| 奉新县| 新晃| 贵港市| 交城县| 安陆市| 彩票| 佛山市| 龙州县| 台安县| 临沭县| 洛阳市|