1. 程式人生 > >【Linux】守護程序以及實現一個守護程序

【Linux】守護程序以及實現一個守護程序

1、什麼是守護程序

守護程序也稱為精靈程序,是執行在後臺程序的一種特殊程序。它獨立於控制終端並且週期性地執行某種任務或等待處理某些發生的事件。
(1)脫離於控制終端並且在後臺執行
(2)不受使用者登入登出的影響,它們一直在執行著
Linux大多數伺服器就是用守護程序實現的,例如:Internet伺服器inetd,Web伺服器httpd

檢視系統中的守護程序:使用命令 ps axj
凡是TPGID一欄中寫著-1的都是沒有控制終端的程序,也就是守護程序。
在COMMAND一列中用 [ ]括起來的名字表示核心執行緒,這些執行緒在核心裡建立,沒有使用者空間程式碼,通常採用K開頭的,表示Kernel。
這裡寫圖片描述

2、實現一個守護程序

(1)遮蔽一些控制終端操作的訊號
這是為了防止守護進行在沒有執行起來前,控制終端受到干擾退出或掛起。
這裡寫圖片描述

(2)呼叫fork,父程序退出
保證子程序不是一個組長程序,方法是在程序中呼叫 fork() 使父程序終止, 讓守護程序在子程序中後臺執行。
這裡寫圖片描述

(3) setsid建立一個新會話
這裡寫圖片描述
該函式返回值:呼叫成功返回新建立的Session的id(就是當且程序的id),出錯返回-1。
注意:呼叫該函式之前,當前程序不允許是程序組的組長,否則返回-1
所以:要保證當前程序不是程序組的組長,先fork建立一個子程序,這樣保證了子程序不可能是該組程序的第一個程序,再呼叫setsid。
成功呼叫setsid函式的結果

  • 建立一個新的Session,當前程序成為Session Leader,當前程序id就是Session的id
  • 建立一個新的程序組,當前程序成為程序組的 Leader,當前程序id就是程序組的id
  • 如果當前程序原本有一個控制終端,則它失去控制終端,成為一個沒有控制終端的程序。

(4) 禁止程序重新開啟控制終端
現在,程序已經成為無終端的會話組長,但它可以重新申請開啟一個控制終端。可以通過使程序不再成為會話組長來禁止程序重新開啟控制終端,採用的方法是再次建立一個子程序,示例程式碼如下:

if( pid=fork() ){ // 父程序  
    exit(0);      // 結束第一子程序,第二子程序繼續(第二子程序不再是會話組長)   
}

(5)關閉開啟的檔案描述符
程序從建立它的父程序那裡繼承了開啟的檔案描述符。如不關閉,將會浪費系統資源,造成程序所在的檔案系統無法卸下以及引起無法預料的錯誤。檔案描述符其實就是 標準輸入0,標準輸出1,錯誤輸出。

close(0);
close(1);
close(2);

(6) 改變當前工作目錄
使用fork建立的子程序是繼承了父程序的當前工作目錄,由於在程序執行中,當前目錄所在的檔案系統是不能解除安裝的,這對以後使用會造成很多的麻煩。因此通常的做法是讓“/”作為守護程序的當前目錄,當然也可以指定其他的別的目錄來作為守護程序的工作目錄。
這裡寫圖片描述

(7)重設檔案建立掩模
程序從建立它的父程序那裡繼承了檔案建立掩模。它可能修改守護程序所建立的檔案的存取許可權。為防止這一點,將檔案建立掩模清除:

umask(0);

(8)處理 SIGCHLD 訊號
但對於某些程序,特別是伺服器程序往往在請求到來時生成子程序處理請求。如果父程序不等待子程序結束,子程序將成為殭屍程序從而佔用系統資源。如果父程序等待子程序結束,將增加父程序的負擔,影響伺服器程序的併發效能。在 Linux 下可以簡單地將 SIGCHLD 訊號的操作設為 SIG_IGN

signal(SIGCHLD,SIG_IGN);

這樣,核心在子程序結束時不會產生殭屍程序。

建立daemon整體程式碼如下:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
int Create_Daemon()
{
    int pid;
    //遮蔽一些控制終端訊號    
    signal(SIGTTOU,SIG_IGN);   
    signal(SIGTTIN,SIG_IGN);   
    signal(SIGTSTP,SIG_IGN);   
    signal(SIGHUP ,SIG_IGN); 
    //設定檔案掩碼
    umask(0);
    //呼叫fork函式,父程序退出
    pid = fork();
    if(pid < 0 )
    {
        printf("error fork");
        return -1;
    }
    else if(pid > 0)
    {//father
        exit(0);
    }
    //設定新會話
    setsid();
    //處理SIGCHlD訊號
    signal(SIGCHLD,SIG_IGN);

    //禁止程序重新開啟控制終端
    if(pid = fork())
    {//father
        exit(0);
    }
    else if(pid <0)
    {
        perror("fork");
        exit(-1);
    }

    //關閉開啟的檔案描述符
    close(0);close(1);close(2);

    //改變當前的工作目錄
    chdir("/");

    return 0;
}

int main()
{
    Create_Daemon();
    while(1);
    return 0;
}

測試結果:
這裡寫圖片描述

3、daemon函式

這裡寫圖片描述

引數:
- nochdir:當 nochdir為零時,當前目錄變為根目錄,否則不變
- noclose:當 noclose為零時,標準輸入、標準輸出和錯誤輸出重導向為/dev/null,也就是不輸出任何信 息,否則照樣輸出。

返回值:deamon()呼叫了fork(),如果fork成功,那麼父程序就呼叫_exit(2)退出,所以看到的錯誤資訊 全部是子程序產生的。如果成功函式返回0,否則返回-1並設定errno。