一江春水向東流

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

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

          編寫Linux/Unix守護進程 [zz]

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

          ?編程要點

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

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

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

          ????這類信號分別是,SIGTTOU(表示后臺進程寫控制終端)、SIGTTIN(表示后臺進程讀控制終端)、SIGTSTP(表示終端掛起)和SIGHUP(進程組長退出時向所有會議成員發(fā)出的)。

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

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

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

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

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

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

          for (fd = 0, fdtablesize = getdtablesize(); 
           fd < fdtablesize; fd++) 
            close(fd);/PRE>
          BR>
          ????但標(biāo)準輸入、標(biāo)準輸出和標(biāo)準錯誤輸出的重定向是可選的。也許有的程序想保留標(biāo)準輸入(0)、標(biāo)準輸出(1)和標(biāo)準錯誤輸出(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.改變工作目錄到根目錄或特定目錄進程活動時,其工作目錄所在的文件系統(tǒng)不能卸下。

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

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

          如果父進程不等待子進程結(jié)束,子進程將成為僵尸進程(zombie)從而占用系統(tǒng)資源。因此需要對SIGCHLD信號做出處理,回收僵尸進程的資源,避免造成不必要的資源浪費??梢杂萌缦抡Z句:
          ????signal(SIGCHLD,(void *)reap_status);

          ????捕捉信號SIGCHLD,用下面的函數(shù)進行處理:

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

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

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

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

          ????下面給出Linux下編程的守護進程的應(yīng)用范例,在UNIX中,不同版本實現(xiàn)的細節(jié)可能不一致,但其實現(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信號,STOP信號 */
           signal(SIGTTOU,SIG_IGN);
           signal(SIGTTIN,SIG_IGN);
            signal(SIGTSTP,SIG_IGN); 
            signal(SIGHUP ,SIG_IGN);
            /* 父進程退出,程序進入后臺運行 */
            if(fork()!=0) exit(1);
             if(setsid()<0)exit(1);/* 創(chuàng)建一個新的會議組 */ 
            /* 子進程退出,孫進程沒有控制終端了 */  
            if(fork()!=0) exit(1);
            if(chdir("/tmp")==-1)exit(1);
          /* 關(guān)閉打開的文件描述符,包括標(biāo)準輸入、標(biāo)準輸出和標(biāo)準錯誤輸出 */ 
           for (fd = 0, fdtablesize = getdtablesize(); fd < fdtablesize; fd++) 
             close(fd);
             umask(0);/*重設(shè)文件創(chuàng)建掩模 */ 
             signal(SIGCHLD,SIG_IGN);/* 忽略SIGCHLD信號 */ 
          /*打開log系統(tǒng)*/
            syslog(LOG_USER|LOG_INFO,"守護進程測試!n");  
             while(1)  
             {  
              time(&now);
             syslog(LOG_USER|LOG_INFO,"當(dāng)前時間:t%sttn",ctime(&now));
              sleep(6);
               }  
           }/PRE>
          BR>
          ????此程序在Turbo Linux 4.0下編譯通過。這個程序比較簡單,但基本體現(xiàn)了守護進程的編程要點。讀者針對實際應(yīng)用中不同的需要,還可以做相應(yīng)的調(diào)整。
          posted on 2007-04-13 14:04 allic 閱讀(994) 評論(0)  編輯  收藏 所屬分類: linux/UNIX 應(yīng)用開發(fā)
          主站蜘蛛池模板: 扎赉特旗| 建水县| 威信县| 玉屏| 盘锦市| 垦利县| 都兰县| 清新县| 理塘县| 河北区| 德化县| 确山县| 稻城县| 筠连县| 黔西县| 会理县| 湖北省| 黎川县| 旬邑县| 伊川县| 呼和浩特市| 保德县| 弥渡县| 潮安县| 蛟河市| 顺平县| 宜昌市| 五河县| 镇康县| 江孜县| 鄂托克旗| 高邮市| 建德市| 平舆县| 锦州市| 湾仔区| 长兴县| 金湖县| 乐都县| 枣强县| 馆陶县|