C語言編寫守護進程

概念

守護進程(daemon)是一種運行在後臺的一種特殊的進程,它獨立於控制終端而且週期性的執行某種任務或等待處理某些發生的事件。因爲在Linux中,每一個系統與用戶進行交流的界面成爲終端,每個今後終端開始運行的進程都會依附於這個終端,這個終端被稱爲這些進程的控制終端,當控制終端被關閉的時候,相應的進程都會自動關閉。可是守護進程卻能突破這種限制,它脫離於終端而且在後臺運行,而且它脫離終端的目的是爲了不進程在運行的過程當中的信息在任何終端中顯示而且進程也不會被任何終端所產生的終端信息所打斷。它從被執行的時候開始運轉,知道整個系統關閉才退出(固然能夠認爲的殺死相應的守護進程)。若是想讓某個進程不由於用戶或中斷或其餘變化而影響,那麼就必須把這個進程變成一個守護進程。shell

實現步驟

  1. 建立子進程,父進程退出(使子進程成爲孤兒進程):這是編寫守護進程的第一步,因爲守護進程是脫離終端的,所以完成第一步後就會在shell終端裏形成一個程序已經運行完畢的假象。以後的全部工做在子進程中完成,而用戶在shell終端裏則能夠執行其餘命令,從而在形式上作到了與控制終端脫離。實現的語句以下:
// 是父進程就結束,而後子進程繼續執行
if(pid = fork()) {
    exit(0);
}
  1. 在子進程中建立新的會話(脫離控制終端):這步是建立守護進程中最重要的一步,雖然實現起來很簡單,可是它的意義很是重要,在這裏使用的是系統函數setsid()來建立一個新的會話,而且擔任該會話組的組長。
  • 進程組(process group):一個或多個進程的集合,每個進程組有惟一一個進程組ID,即進程組長進程的ID。網絡

  • 會話期(session):一個或多個進程組的集合,有惟一一個會話期首進程(session leader)。會話期ID爲首進程的ID。會話期能夠有一個單獨的控制終端(controlling terminal)。與控制終端鏈接的會話期首進程叫作控制進程(controlling process)。當前與終端交互的進程稱爲前臺進程組。其他進程組稱爲後臺進程組。session

  • 掛斷信號(SIGHUP):默認的動做是終止程序。當終端接口檢測到網絡鏈接斷開,將掛斷信號發送給控制進程(會話期首進程)。若是會話期首進程終止,則該信號發送到該會話期前臺進程組。一個進程退出致使一個孤兒進程組中產生時,若是任意一個孤兒進程組進程處於STOP狀態,發送SIGHUP和SIGCONT信號到該進程組中全部進程。所以當網絡斷開或終端窗口關閉後,控制進程收到SIGHUP信號退出,會致使該會話期內其餘進程退出。函數

image

  1. 改變當前目錄爲根目錄:使用fork()建立的子進程是繼承了父進程的當前工做目錄,因爲在進程運行中,當前目錄所在的文件系統是不能卸載的,這對之後使用會形成不少的麻煩。所以一般的作法是讓「/」做爲守護進程的當前目錄,固然也能夠指定其餘的別的目錄來做爲守護進程的工做目錄。this

  2. 重設文件權限掩碼:文件權限掩碼是屏蔽掉文件權限中的對應位。因爲使用fork()函數新建立的子進程繼承了父進程的文件權限掩碼,這就給該子進程使用文件帶了不少的麻煩(好比父進程中的文件沒有執行文件的權限,然而在子進程中但願執行相應的文件這個時候就會出問題)。所以在子進程中要把文件的權限掩碼設置成爲0,即在此時有最大的權限,這樣能夠大大加強該守護進程的靈活性。設置的方法是:umask(0)。編碼

  3. 關閉文件描述符:同文件權限碼同樣,用fork()函數新建的子進程會從父進程那裏繼承一些已經打開了的文件。這些文件被打開的文件可能永遠不會被守護進程讀寫,若是不進行關閉的話將會浪費系統的資源,形成進程所在的文件系統沒法卸下以及引發預料的錯誤。code

  4. 守護進程的退出:
    上面創建了守護進程,當用戶須要外部中止守護進程運行時,每每須要使用kill命令來中止該守護進程,因此守護進程中須要編碼來實現kill發出的signal信號處理,達到進程的正常退出。實現該過程的函數是signal函數:blog

// 將一個給定的函數和一個特定的信號聯繫起來,即在收到特定的信號的時候執行相應的
signal(SIGTERM, sigterm_handler);
void sigterm_handler(int arg) {
    // 進行相應處理的函數                  
}。

例子

#include<stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAXFILE 65535
int main()
{
    pid_t pc;
    int i,fd,len;
    char *buf="this is a dameon \n";
    len = strlen(buf);
    // 建立一個進程用來作守護進程
    // 有看到不少開源服務有fork兩次,但第2次fork不是必須的。
    // fork第二次主要目的是。防止進程再次打開一個控制終端。
    // 由於打開一個控制終端的前臺條件是該進程必須是會話組長。再fork一次,子進程ID != sid(sid是進程父進程的sid)。因此也沒法打開新的控制終端。
    pc =fork(); 
    if(pc<0)
    {
        printf("error fork \n");
        exit(1);
    } else if(pc>0) {
        exit(0);    // 結束父進程
    }
    setsid();       // 使子進程獨立1.擺脫原會話控制 2.擺脫原進程組的控制 3.擺脫控制中端的控制
    chdir("/");     // 改變當前工做目錄,這也是爲了擺脫父進程的影響
    umask(0);       // 重設文件權限掩碼
    for(i = 0;i < MAXFILE; i++)  // 關閉文件描述符(常說的輸入,輸出,報錯3個文件),
        // 由於守護進程要失去了對所屬的控制終端的聯繫,這三個文件要關閉
        close(i);
    while(1)
    {
        if((fd=open("/tmp/dameon.txt",O_CREAT|O_WRONLY|O_APPEND,0600))<0)
        {
            printf("open file err \n");
            exit(0);
        }
        write(fd,buf,len+1);
        close(fd);
        sleep(10);
    }
}
相關文章
相關標籤/搜索