什么是守護進程?


在了解守護進程之前,需要先知道什么是什么是終端?什么是作業?什么是進程組?什么是會話?

在 Linux 中,每一個系統與用戶進行交流的界面稱為終端,每一個從此終端開始運行的進程都會依附於這個終端,這個終端就稱為這些進程的控制終端,當控制終端被關閉時,相應的進程都會自動關閉。

守護進程就是一個生存周期較長,獨立於控制終端並且周期性執行某種任務的進程。之所以要脫離終端,就是為了防止進程運行過程中被任何終端信息所打斷

所以,要創建守護進程,我們就要將這個進程脫離終端。

shell分前后台作業來控制的不是進程而是作業。一個作業由多個進程組成。Shell可以運行一個前台作業和任意多個后台作業,稱為作業控制。bash就是一個獨立的作業。

進程組是一個或多個進程的集合,每個進程除了有一個PID以外,還有一個PGID。PGID就是組長的PID。進程組通常和一個作業相關聯,可以接收來自同一個終端的信號。

當然,進程組和作業也並不是完全等價的兩個概念:如果作業中某個進程有創建了新的子進程,該子進程不屬於作業,但屬於該進程組。

會話(Session)是一個或多個進程組的集合。一個會話可以有一個控制終端。一個會話中,有一個前台作業和若干個后台作業。會話SID是會話手進程的PID。

為什么只能運行一個前台作業?當我們在前台新起了一個作業,shell就被提到了后台,因此shell就沒有辦法再繼續接受我們的指令並且解析運行了。但是如果前台進程退出了,shell就會有被提到前台來,就可以繼續接受我們的命令並且解析運行。

那么,如何來切斷進程和終端的關系呢?

首先,調用 setsid() 使子進程成為新的會話組長。setsid() 調用成功后,進程成為新的會話組長和新的進程組長,並與原來的登錄會話和進程組脫離。

調用setsid()有一個前提,就是該進程不能是一個組長進程,因此需要先fork並且殺死父進程,setsid ()的調用者是子進程。

接下來,要禁止進程重新打開控制終端。能打開控制終端的進程一定是進程組組長,因此我們需要再次fork(),並且殺死父進程,留下的子進程就不再是話首進程和進程組組長。於是,這個子進程也不再擁有打開終端的權限,至此,我們徹底切斷了該進程和終端的聯系。

最后,要關閉打開的文件描述符,或者對打開的文件描述符進行重定向。因為進程會繼承從父進程那里的文件描述符,如果不關閉,會浪費系統的資源。

如果想改變該進程的所在目錄,可以調用chdir("/") 將該守護進程轉移到根目錄。

如果該守護進程有子進程,那么守護進程需要等待子進程退出,否則子進程會變成僵屍進程。為了減少該守護進程的負擔,防止其回收子進程對服務器並發性能的影響,可以使用signal(SIGCHLD, SIG_IGN) 對SIGCHLD忽略。這樣就可以防止僵屍進程產生。

#include <unistd.h>   
#include <signal.h>   
#include <fcntl.h>  
#include <sys/syslog.h>  
#include <sys/param.h>   
#include <sys/types.h>   
#include <sys/stat.h>   
#include <stdio.h>  
#include <stdlib.h>  
#include <time.h>  
  
int init_daemon(void)  
{   
    int pid;   
    int i;  
      
    // 1)屏蔽一些控制終端操作的信號  
    signal(SIGTTOU,SIG_IGN);   
    signal(SIGTTIN,SIG_IGN);   
    signal(SIGTSTP,SIG_IGN);   
    signal(SIGHUP ,SIG_IGN);  
   
    // 2)在后台運行  
    if( pid=fork() ){ // 父進程  
        exit(0); //結束父進程,子進程繼續  
    }else if(pid< 0){ // 出錯  
        perror("fork");  
        exit(EXIT_FAILURE);  
    }  
      
    // 3)脫離控制終端、登錄會話和進程組  
    setsid();    
      
    // 4)禁止進程重新打開控制終端,這是一種防御性編程,是可選的一步
    if( pid=fork() ){ // 父進程  
        exit(0);      // 結束第一子進程,第二子進程繼續(第二子進程不再是會話組長)
     
    }else if(pid< 0){ // 出錯  
        perror("fork");  
        exit(EXIT_FAILURE);  
    }    
      
    // 5)關閉打開的文件描述符  
    // NOFILE 為 <sys/param.h> 的宏定義  
    // NOFILE 為文件描述符最大個數,不同系統有不同限制  
    for(i=0; i< NOFILE; ++i){  
        close(i);  
    }  
      
    // 6)改變當前工作目錄  
    chdir("/tmp");   
      
    // 7)重設文件創建掩模,因為進程從創建它的父進程那里繼承了文件創建掩模。它可能修改守護進程所創建的文件的存取權限。
    umask(0);    
      
    // 8)處理 SIGCHLD 信號  
    signal(SIGCHLD,SIG_IGN);  
      
    return 0;   
}   
  
int main(int argc, char *argv[])   
{  
    init_daemon();  
      
    while(1);  
  
    return 0;  
} 


免責聲明!

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



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