服務器編程入門(12) 守護進程


摘要:

    在之前的《服務器編程入門(5)Linux服務器程序規范》中,我們提到過將服務器程序后台化,這就是本節將要討論的守護進程. 

    本節主要關注一下問題:  

    1 什么是守護進程?

    2 守護進程的啟動方法有哪些?

    3 如何創建一個守護進程?


1 什么是守護進程?

在后台運行,且不與任何控制終端關聯的進程。

守護進程不與作業控制、終端會話管理、終端產生信號等發生交互,也可以避免在后台運行的守護進程非預期地輸出到終端。

兩個特點:

  • 守護進程執行中的信息不顯示在任何一個終端上
  • 守護進程不被終端產生的無用信號所中斷

在理解更多關於守護進程的概念之前,我們先了解一下進程、進程組、會話期和控制終端的關系

  • 每一個進程有一個進程ID,每個進程都屬於一個進程組
  • 每個進程組有一個組長進程組長進程的ID等於進程組ID
  • 會話期是一個或多個進程組的集合,一個會話期可以有一個單獨的控制終端(其中,只有一個前台進程組可以控制終端的交互)
  • 從shell中啟動的每個進程將繼承一個終端,以便進程與用戶交互,同時繼承父進程的會話期和進程組ID,因此子進程會受發給該會話期或進程組的信號的影響。

守護進程與普通進程的區別如下圖所示:

守護進程

 


2 守護進程的啟動方法有哪些?

  1. 在系統啟動階段,許多守護進程又系統初始化腳本啟動。這些腳本通常位於/etc目錄或以/etc/rc開頭的某個目錄中。由這些腳本啟動的守護進程一開始時就擁有超級用戶特權。
  2. 許多網絡服務器由inetd超級服務器啟動。
  3. cron守護進程按照規則定期執行一些程序。由它啟動執行的程序同樣作為守護進程運行。
  4. at命令用於指定將來某個時刻運行程序,由它啟動的程序同樣作為守護進程。
  5. 從用戶終端或前台或后台啟動。

3 使用庫函數daemon創建守護進程

首先我們使用庫函數daemon創建守護進程,然后研究一下守護進程的創建過程,並實現一個守護進程化函數,達到和庫函數daemon相同的效果。

函數:daemon

聲明:

#include <unistd.h>
int daemon(int nochdir, int noclose);

作用:通過在服務器程序中調用它,可以把一個普通進程轉變為守護進程。

參數說明:

If nochdir is zero, daemon()  changes  the  process’s  current  working directory to the root directory ("/"); otherwise,

If  noclose is zero, daemon() redirects standard input, standard output and standard error to /dev/null; otherwise,  no  changes  are  made  to these file descriptors.

Demo:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <string.h>
#include <error.h>

int main(int argc, char* argv[]) {
    time_t t;
    int fd;
    
    // 將當前進程變成守護進程
    if (daemon(0, 0) == -1) {
        perror("daomon error");
        exit(EXIT_FAILURE);
    }

    while(1) {
        //這時工作目錄已經被daemon函數切換到了系統根目錄下
        fd = open("daemon.log", O_WRONLY|O_CREAT|O_APPEND, 0644);   
        if (fd == -1) {
            perror("open daemon.log error");
            exit(EXIT_FAILURE);
        }
        t = time(0);
        char *buf = asctime(localtime(&t));
        write(fd, buf, strlen(buf));     //向daemon.log文件中寫入當前時間
        close(fd);
        sleep(60);    // 每隔60s寫入一次
    }
}

運行截圖:

image

執行ps命令發現,並沒有名為testDaemon的守護進程,主要原因是daemon函數會將當前工作目錄切換到/目錄下,而普通用戶沒有權限在系統根目錄創建文件。

所以實際上出錯在fd = open("daemon.log", O_WRONLY|O_CREAT|O_APPEND, 0644); 這里,又因為守護進程是不和當前終端交互的,所以沒有看到報錯信息。

現在我們切換到root用戶執行程序,運行截圖:

image

 


4 創建守護進程過程分析,用自己實現的myDaemon函數創建守護進程

守護進程創建過程:

守護進程創建流程

代碼實現:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <string.h>
#include <error.h>
#include <signal.h>

#define MAXFD 64
extern int daemon_proc;

int myDaemon(int ,int );

int main(int argc, char* argv[]) {
    time_t t;
    int fd;
    
    if (myDaemon(0, 0) == -1) {
        perror("daomon error");
        exit(EXIT_FAILURE);
    }

    while(1) {
        fd = open("daemon.log", O_WRONLY|O_CREAT|O_APPEND, 0644);
        if (fd == -1) {
            perror("open daemon.log error");
            exit(EXIT_FAILURE);
        }
        t = time(0);
        char *buf = asctime(localtime(&t));
        write(fd, buf, strlen(buf));
        close(fd);
        sleep(60);
    }
    fprintf(stderr,
            "Hello world!\n");
}


int myDaemon(int nochdir, int noclose) {
    int i;
    pid_t pid;

    if ( (pid = fork()) < 0 )
        return -1;
    else if (pid) {        /* parent terminated */
        _exit(0);
    }

    /* child 1 continues... */
    if (setsid() < 0)     /* become session leader */
        return -1;

    signal(SIGHUP, SIG_IGN);    /* ignore SIGHUP singal */
    if ( (pid = fork()) < 0 )
        return -1;
    else if (pid) {
        _exit(0);         /* child 1 terminated */
    }

    /* child 2 continues... */
    daemon_proc = 1;      /* use syslog instead of fprintf to stderr */
    
    if (nochdir == 0)
        chdir("/");           /* change working directory */
    
    if (noclose == 0) {
        /*close off file descriptors*/
        for (i = 0; i < MAXFD; i++)
            close(i);
    
        /* redirect stdin, stdout, and stderr to /dev/null */
        open("/dev/null", O_RDONLY);
        open("/dev/null", O_RDWR);    
        open("/dev/null", O_RDWR);
    }
    umask(0);
}

 

運行截圖:

image

 

 

 

 

參考資料:

《UNIX網絡編程 卷1:套接字聯網API(第3版)》

linux系統編程之進程(八):守護進程詳解及創建,daemon()使用

Linux內核中的進程組及會話


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM