《unix環境高級編程》-13、守護進程-讀書筆記

一、基本概念

1、守護進程也成爲精靈進程,是生存週期較長的一種進程。它們常常在系統自舉時啓動,在系統關閉時才終止。因爲沒有控制終端,所以說它們是在後臺運行的。

2、父進程ID爲0的各進程通常是內核進程,它們作爲系統自舉過程都得一部分而啓動。

3、大多數守護進程都以超級用戶(用戶ID爲0)特權運行。沒有一個守護進程具有控制終端,其終端名設置爲問號(?),終端前臺進程組ID設置爲-1。內核守護進程以無控制終端方式啓動。用戶層守護進程缺少控制終端可能是守護進程調用了setsid的結果。所有用戶層守護進程都是進程組的組長進程以及會話的首進程,而且是這些進程組和會話中的唯一進程。最後,注意,大多數守護進程的父進程是init進程。


注:

沒有終端限制
  讓某個進程不因爲用戶、終端或者其他的變化而受到影響,那麼就必須把這個進程變成一個守護進程


二、編程規則:

(1)首先要做的是調用umask將文件模式創建屏蔽字設置爲0。由繼承得來的文件模式創建屏蔽字可能會拒絕設置某些權限

例如,若守護進程要創建一組可讀、可寫的文件,而繼承的文件模式創建屏蔽字可能屏蔽了這兩種權限,於是所要求的組可讀、寫就不能起作用。

(2)調用fork,然後使父進程退出(exit)。這樣做實現了以下幾點:第一:如果守護進程是作爲一條簡單shell命令啓動的,但具有一個新的ID,這就保證了子進程不是一個進程組的組長進程。這對於下面就要做的setsid調用是必要的前提條件。

(3)調用setsid以創建一個新會話。於是執行三個操作:

使得調用進程完成以下動作:

--稱爲新會話的首進程

--稱爲一個新進程組的組長進程

--沒有控制終端

(4)將當前工作目錄更改爲根目錄。從父進程繼承過來的當前工作目錄可能在一個裝配文件 系統中。因爲守護進程通常在系統再引導之前是一直存在的,所以如果守護進程的當前工作目錄在一個裝配文件系統中,那麼文件系統就不能被拆卸。這與裝配文件系統的原意不符。

    另外,某些守護進程可能會把當前工作目錄更改到某個指定位置,在那裏做它們的工作。

(5)關閉不再需要的文件描述符。這使得守護進程不再持有從其父進程繼承來的某些文件描述符(父進程可能是shell進程或者其他某個進程)。

(6)某些守護進程打開/dev/null使其具有文件描述符0、1和2,這樣,任何一個試圖讀標準輸入、寫標準輸出或標準出錯的庫例程都不會產生任何效果。

因爲守護進程並不與終端設備相關聯,所以不能再終端設備上顯示其輸出,也無處從交互式用戶那裏接受輸入。

即使守護進程是從交互式會話啓動的,但因爲守護進程是後臺運行的,所以登錄會話的終止並不影響守護進程。如果其他用戶在同一終端設備上登錄,我們也不會見到守護進程的輸出,用戶也不希望他們在終端上的輸入會由守護進程讀取。



注:某些時候可能需要處理SIGCHLD信號:

雖然處理SIGCHLD並不是必須的,但是對於某些進程,特別是服務器進程往往在請求到來時生成子進程處理請求。如果父進程不等待子進程結束,子進程將成爲殭屍進程(zombie)從而佔用系統資源。如果父進程等待子進程結束,將增加父進程 的負擔,影響服務進程的併發性能。

在Linux下可以簡單的將SIGCHLD信號的操作設爲SIG_IGN。

signal(SIGCHLD, SIG_IGN);

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


轉:

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

void my_daemon() {
    int pid, fd;

    // 1.轉變爲後臺進程
    if ((pid = fork()) == -1) exit(1);
    if (pid != 0) exit(0); // 父進程(前臺進程)退出

    // 2.離開原先的進程組,會話
    if (setsid() == -1) exit(1); // 開啓一個新會話

    // 3.禁止再次打開控制終端
    if ((pid = fork()) == -1) exit(1);
    if (pid != 0) exit(0); // 父進程(會話領頭進程)退出

    // 4.關閉打開的文件描述符,避免浪費系統資源
    for (int i = 0; i < NOFILE; i++)
        close(i);

    // 5.改變當前的工作目錄,避免卸載不了文件系統
    if (chdir("/") == -1) exit(1);

    // 6.重設文件掩碼,防止某些屬性被父進程屏蔽
    if (umask(0) == -1) exit(1);

    // 7.重定向標準輸入,輸出,錯誤流,因爲守護進程沒有控制終端
    if ((fd = open("/dev/null", O_RDWR)) == -1) exit(1); // 打開一個指向/dev/null的文件描述符
    dup2(fd, STDIN_FILENO);
    dup2(fd, STDOUT_FILENO);
    dup2(fd, STDERR_FILENO);
    close(fd);

    // 8.本守護進程的子進程若不需要返回信息,那麼交給init進程回收,避免產生殭屍進程
    if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) exit(1);
}

#define INTERVAL 2

int main(int argc, char *argv[]) {
    my_daemon(); // 首先使之成爲守護進程

    int t = 0;
    FILE *fp = fopen("/root/tmp.txt", "a");
    fprintf(fp, "ppid = %d, pid = %d, sid = %d, pgrp = %d\n", getppid(), getpid(), getsid(0), getpgrp());
    fflush(fp);

    do { // 測試此後臺進程,每INTERVAL秒打印當前時間t,30秒後退出此後臺進程
        fprintf(fp, "%d\n", t);
        fflush(fp); // 輸出緩衝區內容到文件中
        sleep(INTERVAL);
        t += INTERVAL;
    } while(t < 30);

    fclose(fp);

    return 0;
}

保存爲daemon.c
編譯命令 gcc daemon.c
運行 ./a.out
查看tmp.txt文件內容 cat /root/tmp.txt




三、出錯記錄

    由於守護進程沒有控制終端,所以需要有一個集中的守護進程出錯記錄設施。



四、守護進程慣例

在UNIX系統中,守護進程遵循以下公共慣例:

1、若守護進程使用鎖文件,那麼該文件通常存放在/var/run目錄中,注意,守護進程可能需要具有超級用戶權限才能在此目錄下創建文件。鎖文件的名字通常是name.pid,其中,name是該守護進程或服務的名字。例如cron守護進程鎖文件的的名字是/var/run/cron.pid


2、若守護進程支持配置選項,那麼配置文件通常存放在/etc目錄中。配置文件的名字通常是name.conf,其中,name是該守護進程或服務的名字。syslogd守護進程的配置文件是/etc/syslog.conf


3、守護進程可用命令行啓動,但通常它們是由系統初始化腳本之一(/etc/rc* 或 /etc/init.d/* )啓動的。如果在守護進程終止時,應當自動重啓它,則我們可以再/etc/inittab中爲該守護進程包括_respawn記錄項,這樣,init就將重啓該守護進程。


4、若一個守護進程有一個配置文件,那麼當該守護進程啓動時,它讀該文件,但在此之後一般不會再查看它。





















發佈了130 篇原創文章 · 獲贊 40 · 訪問量 82萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章