守護進程的編寫

守護進程的編寫

ps: 參考資料:linux C++通信架構實戰 卷1nginx

一,普通進程

查看進程bash

ps -eo pid,ppid,sid,tty,pgrp,comm,stat,cmd | grep -E 'bash|PID|nginx'
  • 這種進程都有各自對應的終端,終端退出,該進程也就退出,它的父進程是一個bash
  • 終端被佔用,你輸入命令沒有做用
  • 若是放到後臺(bg), 輸入命令有用了,可是終端關閉進程仍然會退出

二,守護進程

定義:一種長期在後臺運行的進程,不與任何終端關聯架構

  • Linux自己是有不少守護進程默默運行着。查看ps -efj
    • ppid = 0 內核進程,跟隨系統,生命週期貫穿這個系統。
    • cmd 帶 [ ] 的是內核守護進程
    • 老祖init,也是系統守護進程,它負責啓動各運行層次特定的系統服務;因此不少進程的PPID是init。並且這個init也負責收養孤兒進程;
    • cmd列中名字不帶[]的普通守護進程(用戶級守護進程)
  • 共同特色
    • 大多數守護進程都是以超級 用戶特權運行的;
    • 守護進程沒有控制終端,TT這列顯示?
    • 內核守護進程以無控制終端方式啓動
    • 普通守護進程多是守護進程調用了setsid的結果(無控制端);

編寫規則函數

  • 調用umask(0);
    • umask是個函數,用來限制(屏蔽)一些文件權限的。
  • fork()一個子進程(脫離終端)出來,而後父進程退出( 把終端空出來,不讓終端卡住);固定套路
    • fork()的目的是想成功調用setsid()來創建新會話,目的是子進程有單獨的sid;
    • 並且子進程也成爲了一個新進程組的組長進程;同時,子進程不關聯任何終端了;

在編寫前還須要知道一些相關概念操作系統

  • 文件描述符
    • 當你打開一個存在的文件或者建立一個新文件,操做系統都會返回這個文件描述符(其實就是表明這個文件的),後續對這個文件的操做的一些函數,都會用到這個文件描述符做爲參數;
    • linux中三個特殊的文件描述符,數字分別爲0,1,2
      • 0:標準輸入【鍵盤】,對應的符號常量叫STDIN_FILENO
      • 1:標準輸出【屏幕】,對應的符號常量叫STDOUT_FILENO
      • 2:標準錯誤【屏幕】,對應的符號常量叫STDERR_FILENO
    • 類Unix操做系統,默認從STDIN_FILENO讀數據,向STDOUT_FILENO來寫數據,向STDERR_FILENO來寫錯誤;
    • 萬物皆文件
    • 你程序一旦運行起來,這三個文件描述符0,1,2會被自動打開(自動指向對應的設備);
//文件描述符雖然是數字,可是,若是咱們把文件描述符直接理解成指針(指針裏邊保存的是
//地址——地址說白了也是個數字);
write(STDOUT_FILENO,"aaaabbb",6);
  • 輸入輸出重定向
    • 之前老寫的freopen應該是同一個原理。
    • 在Linux命令行中的 > < 能夠重定向輸出
  • 空設備 【 /dev/null 】
    • 一個特殊的文件設備,重定向過去的東西都會被吸取(黑洞)

  • 守護進程不會收到的信號:內核發給你,另外的進程發給你的
    • SIGHUP信號
      • 守護進程不會收到來自內核的 SIGHUP 信號; 潛臺詞就是 若是守護進程收到了 SIGHUP信號,那麼確定是另外的進程發給你的;
      • 不少守護進程把這個信號做爲通知信號,表示配置文件已經發生改動,守護進程應該從新讀入其配置文件;
    • SIGINT、SIGWINCH信號
      • 守護進程不會收到來自內核的 SIGINT(ctrl+C),SIGWINCH(終端窗口大小改變) 信號;
  • 守護進程和後臺進程的區別
    • 守護進程和終端不掛鉤;後臺進程能往終端上輸出東西(和終端掛鉤);
    • 守護進程關閉終端時不受影響,守護進程會隨着終端的退出而退出;

案例代碼命令行

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>

//建立守護進程
//建立成功則返回1,不然返回-1
int ngx_daemon()
{
    int  fd;

    switch (fork())  //fork()子進程
    {
    case -1:
        //建立子進程失敗,這裏能夠寫日誌......
        return -1;
    case 0:
        //子進程,走到這裏,直接break;
        break;
    default:
        //父進程,直接退出 
        exit(0);         
    }

    //只有子進程流程才能走到這裏
    if (setsid() == -1)  //脫離終端,終端關閉,將跟此子進程無關
    {
        //記錄錯誤日誌......
        return -1;
    }
    umask(0); //設置爲0,不要讓它來限制文件權限,以避免引發混亂

    fd = open("/dev/null", O_RDWR); //打開黑洞設備,以讀寫方式打開
    if (fd == -1) 
    {
        //記錄錯誤日誌......
        return -1;
    }
    if (dup2(fd, STDIN_FILENO) == -1) //先關閉STDIN_FILENO[這是規矩,已經打開的描述符,動他以前,先close],相似於指針指向null,讓/dev/null成爲標準輸入;
    {
        //記錄錯誤日誌......
        return -1;
    }

    if (dup2(fd, STDOUT_FILENO) == -1) //先關閉STDIN_FILENO,相似於指針指向null,讓/dev/null成爲標準輸出;
    {
        //記錄錯誤日誌......
        return -1;
    }

     if (fd > STDERR_FILENO)  //fd應該是3,這個應該成立
     {
        if (close(fd) == -1)  //釋放資源這樣這個文件描述符就能夠被複用;否則這個數字【文件描述符】會被一直佔着;
        {
            //記錄錯誤日誌......
            return -1;
        }
    }

    return 1;
}

int main(int argc, char *const *argv)
{
    if(ngx_daemon() != 1)
    {
        //建立守護進程失敗,能夠作失敗後的處理好比寫日誌等等
        return 1; 
    } 
    else
    {
        //建立守護進程成功,執行守護進程中要乾的活
        for(;;)
        {        
            sleep(1); //休息1秒
            printf("休息1秒,進程id=%d!\n",getpid()); //你就算打印也沒用,如今標準輸出指向黑洞(/dev/null),打印不出任何結果【不顯示任何結果】
        }
    }
    return 0;
}
相關文章
相關標籤/搜索