編寫Linux/Unix守護進程(比較全)

來源:互聯網
上載者:User
守護進程在Linux/Unix系統中有著廣泛的應用。有時,開發人員也想把自己的程式變成守護進程。在建立一個守護進程的時候,要接觸到子進程、進程組、會晤期、訊號機制、檔案、目錄和控制終端等多個概念。因此守護進程還是比較複雜的,在這裡詳細地討論Linux/Unix的守護進程的編寫,總結出八條經驗,並給出應用範例。 

    編程要點 

    1.屏蔽一些有關控制終端操作的訊號。防止在守護進程沒有正常運轉起來時,控制終端受到幹擾退出或掛起。樣本如下: 


signal(SIGTTOU,SIG_IGN);            signal(SIGTTIN,SIG_IGN);            signal(SIGTSTP,SIG_IGN);            signal(SIGHUP ,SIG_IGN);

    所有的訊號都有自己的名字。這些名字都以“SIG”開頭,只是後面有所不同。開發人員可以通過這些名字瞭解到系統中發生了什麼事。當訊號出現時,開發人員可以要求系統進行以下三種操作:
    ◆ 忽略訊號。大多數訊號都是採取這種方式進行處理的,這裡就採用了這種用法。但值得注意的是對SIGKILL和SIGSTOP訊號不能做忽略處理。
    ◆ 捕捉訊號。最常見的情況就是,如果捕捉到SIGCHID訊號,則表示子進程已經終止。然後可在此訊號的捕捉函數中調用waitpid()函數取得該子進程的進程ID和它的終止狀態。另外,如果進程建立了臨時檔案,那麼就要為進程終止訊號SIGTERM編寫一個訊號捕捉函數來清除這些臨時檔案。
    ◆ 執行系統的預設動作。對絕大多數訊號而言,系統的預設動作都是終止該進程。 

    對這些有關終端的訊號,一般採用忽略處理,從而保障了終端免受幹擾。 

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

    2.將程式進入後台執行。由於守護進程最終脫離控制終端,到後台去運行。方法是在進程中調用fork使父進程終止,讓Daemon在子進程中後台執行。這就是常說的“脫殼”。子進程繼續函數fork()的定義如下: 


#include <sys/types.h>            #include <unistd.h>            pid_t fork(void);

    該函數是Linux/Unix編程中非常重要的函數。它被調用一次,但返回兩次。這兩次返回的區別是子進程的傳回值為“0”,而父進程的傳回值為子進程的ID。如果出錯則返回“-1”。 

    3.脫離控制終端、登入工作階段和進程組。開發人員如果要擺脫它們,不受它們的影響,一般使用 setsid() 設定新會話的領頭進程,並與原來的登入工作階段和進程組脫離。這隻是其中的一種方法,也有如下處理的辦法: 


if  ((fd = open("/dev/tty",O_RDWR)) >= 0) {            ioctl(fd,TIOCNOTTY,NULL);            close(fd);            }

    其中/dev/tty是一個流裝置,也是終端映射,調用close()函數將終端關閉。 

    4.禁止進程重新開啟控制終端。進程已經成為無終端的交談群組長,但它可以重新申請開啟一個控制終端。開發人員可以通過不再讓進程成為交談群組長的方式來禁止進程重新開啟控制終端,需要再次調用fork函數。
    上面的程式碼表示結束第一子進程,第二子進程繼續(第二子進程不再是交談群組長)。 

    5. 關閉開啟的檔案描述符,並重新導向標準輸入、標準輸出和標準錯誤輸出的檔案描述符。進程從建立它的父進程那裡繼承了開啟的檔案描述符。如果不關閉,將會浪費系統資源,引起無法預料的錯誤。關閉三者的代碼如下: 


for (fd = 0, fdtablesize = getdtablesize();            fd < fdtablesize; fd++)            close(fd);

    但標準輸入、標準輸出和標準錯誤輸出的重新導向是可選的。也許有的程式想保留標準輸入(0)、標準輸出(1)和標準錯誤輸出(2),那麼迴圈應繞過這三者。代碼如下: 


for (fd =3, fdtablesize = getdtablesize();            fd < fdtablesize; fd++)            close(fd);

    有的程式有些特殊的需求,還需要將這三者重新定向。樣本如下: 


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);

    6.改變工作目錄到根目錄或特定目錄進程活動時,其工作目錄所在的檔案系統不能卸下。 

    一般需要將工作目錄改變到根目錄或特定目錄,注意使用者對此目錄需要有讀寫權。防止超級使用者卸載裝置時系統報告裝置忙。 

    7.處理SIGCHLD訊號。SIGCHLD訊號是子進程結束時,向核心發送的訊號。 

如果父進程不等待子進程結束,子進程將成為殭屍進程(zombie)從而佔用系統資源。因此需要對SIGCHLD訊號做出處理,回收殭屍進程的資源,避免造成不必要的資源浪費。可以用如下語句:
    signal(SIGCHLD,(void *)reap_status); 

    捕捉訊號SIGCHLD,用下面的函數進行處理: 


void reap_status()            { int pid;            union wait status;            while ((pid = wait3(&status,WNOHANG,NULL)) > 0)            …… }

    8.在Linux/Unix下有個syslogd的守護進程,向使用者提供了syslog()系統調用。任何程式都可以通過syslog記錄事件。 

    由於syslog非常好用和易配置,所以很多程式都使用syslog來發送它們的記錄資訊。一般守護進程也使用syslog向系統輸出資訊。syslog有三個函數,一般只需要用syslog(...)函數,openlog()/closelog()可有可無。syslog()在shslog.h定義如下: 


#include <syslog.h>            void syslog(int priority,char *format,...);

    其中參數priority指明了進程要寫入資訊的等級和用途。第二個參數是一個格式串,指定了記錄輸出的格式。在這個串的最後需要指定一個%m,對應errno錯誤碼。 

    應用範例 

    下面給出Linux下編程的守護進程的應用範例,在UNIX中,不同版本實現的細節可能不一致,但其實現的原則是與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);/* 建立一個新的會議組 */            /* 子進程退出,孫進程沒有控制終端了 */            if(fork()!=0) exit(1);            if(chdir("/tmp")==-1)exit(1);            /* 關閉開啟的檔案描述符,包括標準輸入、標準輸出和標準錯誤輸出 */            for (fd = 0, fdtablesize = getdtablesize(); fd < fdtablesize; fd++)            close(fd);            umask(0);/*重設檔案建立掩模 */            signal(SIGCHLD,SIG_IGN);/* 忽略SIGCHLD訊號 */            /*開啟log系統*/            syslog(LOG_USER|LOG_INFO,"守護進程測試!\n");            while(1)            {            time(&now);            syslog(LOG_USER|LOG_INFO,"目前時間:\t%s\t\t\n",ctime(&now));            sleep(6);            }            }

    此程式在Turbo Linux 4.0下編譯通過。這個程式比較簡單,但基本體現了守護進程的編程要點。讀者針對實際應用中不同的需要,還可以做相應的調整。

 

 

 

來自:http://www.360doc.com/content/10/0610/13/61151_32311588.shtml

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.