1. 程式人生 > >Linux程序間關係之守護程序

Linux程序間關係之守護程序

概念

守護程序也稱精靈程序,是執行在後臺的一種特殊程序。守護程序獨立於控制終端並且週期性的執行某種任務或者等待處理某些打算的事件。可認為守護程序目的就是防止終端產生的一些訊號讓程序退出

特點

  • 所有的守護程序都沒有控制終端,其終端名(TTY)設定為問號(?)。
  • 自成會話,自成程序組。不與其他會話或程序組相互關聯,干擾。所以一般一個守護程序的程序ID,組ID,會話ID都相同。(自成程序組這點說的也不太嚴謹,若父程序是守護程序,父程序fork的子程序也是守護程序。這時父子程序屬於同一程序組)
  • 命令以‘d’結尾
  • 守護程序不受使用者登入登出的影響,當你登出或者重登後,守護程序一直在執行。
  • 生存期長,在系統引導裝入時啟動,僅在系統關閉時終止。
  • 在後臺執行(原因可歸結於沒有控制終端)
  • 大多數的守護程序都以root特權執行。

Linux大多數伺服器都是由守護程序實現。如:web,http,阿帕奇等。
守護也完成許多系統任務,如:作業規劃程序crond。

使用者層守護程序

使用者層沒有終端的原因可能是呼叫了setsid(會在下面細講)函式的結果。大多數使用者層守護程序都是程序組的組長程序以及會話的會話首程序,而且是這些程序組和會話中的唯一程序(rsyslogd是一個例外)。

這裡寫圖片描述

我們通常所說的1號程序init就是一個系統守護程序。除了其他工作外,主要負責啟動各執行層析的系統服務。這些服務通常是在它們自己擁有的守護程序的幫助下實現的。此外,init就是使用者層程序的父程序。
(上圖中的各選項與本圖中的頭部一一對應)
這裡寫圖片描述

核心執行緒(核心守護程序)

(其實也叫核心程序,Linux沒有真正意義上的執行緒,都是用程序模擬實現,輕量級程序。)

核心守護程序以無終端的方式啟動,凡是在TPGID一欄寫著-1的程序都是沒有控制終端的程序,也就是守護程序。在COMMAND列[ ]括起來的名字表示核心執行緒,這些執行緒在核心建立,沒有使用者空間程式碼,因此沒有程式檔名和命令列, 通常採用以k開頭的名字,表Kernel。對於需要在程序上下文執行工作但卻不被使用者層程序上下文呼叫的每一個核心元件,通常都有它自己的核心守護程序。
這裡寫圖片描述

Linux通常使用一個叫kthreadd的特殊核心程序來建立其他核心程序,所以kthreadd表現為其他核心程序的父程序。

這裡寫圖片描述
由此我們可看到kthread的程序ID為2。這也就解釋了為什麼在上圖中核心程序(核心執行緒)的父程序ID(PPID)都為2了。

建立守護程序

函式setsid,建立守護程序很關鍵的一步就是呼叫setsid函式建立一個新的會話(Session),並讓當前的程序稱為這個會話的Leader,即會話首程序。

#include<stdio.h>
pid_t setsid(void)

返回值:若成功,返回程序組ID;若失敗,返回-1。

執行方式:直接在要設定的程序裡呼叫。

這個函式的執行結果為:

  • 建立一個新會話,使該程序變成新會話的會話首程序(會話首程序也可理解為建立會話的程序),此時,該程序是當前會話的唯一程序
  • 使該程序稱為一個新程序組的組長程序,程序組ID就是該程序的程序ID。
  • 使該程序沒有控制終端,如果在呼叫setsid之前有,那麼就切斷控制終端與當前程序的聯絡。
  • 當前程序的程序ID,程序組ID,會話ID都相等。

setsid函式呼叫之前有一個特殊要求:該程序不能是一個程序組的組長程序。如果是,函式呼叫將返回出錯。

所以我們通常為了防止出現這種情況會用以下做法:讓一個程序fork出一個子程序,然後立即將父程序終止,而子程序繼續。子程序的程序ID是新分配的,子程序的PCB集成了父程序的程序組ID,所以兩者不可能相等。這樣就保證了子程序不是一個程序組的組長。

除了以上兩種操作外,建立一個守護程序還需要其他幾項工作。下面我將完全的步驟列出來:

  • 呼叫umask函式將當前檔案模式建立遮蔽字為一個已知值(通常為0)。在上面提到過,我們要操作的程序是一個子程序。而子程序從父程序的PCB繼承過來的檔案模式建立遮蔽字可能會遮蔽某些許可權。而加入我們要建立的守護程序正好需要這些許可權的話就會造成很麻煩的問題。另一方面,如果守護程序呼叫了庫函式建立了檔案,那麼檔案模式建立遮蔽字應該設定為更強的(如007)。因為庫函式不允許呼叫者通過一個顯式的函式引數來設定許可權。
  • fork子程序,並且結束父程序。
  • 呼叫setsid函式。建立一個會話,使當前程序稱為一個會話的首程序,一個程序組的組長程序,一個沒有控制終端的程序。
  • 呼叫函式chdir將當前程序的工作目錄更改為根目錄或者某個指定位置。因為子程序從父程序繼承來的工作目錄可能是在一個掛載的檔案系統中,而守護程序在系統再次引導前是一直存在的,如果不更改,那麼掛載的檔案系統就一直解除安裝不了。
  • 呼叫fclose函式關閉不在需要的檔案描述符(0,1,2等)。使守護程序不再持有從父程序繼承來的任何檔案描述符。或者還可以將檔案描述符重定向到檔案(/dev/null)。這樣相當於將當前程序的標準輸入,標準輸出,標準錯誤都失效。關閉檔案描述符的原因是守護程序是與控制終端沒有任何聯絡的,並且它是在後臺執行。後臺並沒有接受它輸入輸出也無處顯示,我們不希望在終端上簡單守護程序的輸出,使用者也不想在終端上的輸入被守護程序讀取。
fd0 = open("/dev/null",O_RDWR);
dup2(fd0,1); //dup2的作用就是檔案描述符重定向
dup2(fd0,2);

建立例項

void mydaemon()
{
    umask(0);
    pid_t id = fork();
    if(id > 0 )
    {
        exit(1);
    }

    printf("Debug\n");
    setsid(); //建立新會話

    chdir("/"); //將工作目錄設定為根目錄

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

    signal(SIGCHLD,SIG_IGN);//忽略SIGCHLD訊號

}

int main()
{
    mydaemon();
    while(1); //死迴圈,方便我們檢視
    return 0;
}

編譯執行後我們可以利用ps axj指令檢視這個守護程序。

這裡寫圖片描述

上面為我們建立守護程序的一種方式,其實Linux為我們提供了專門的函式介面來建立守護程序。

函式daemon():

#include <unistd.h>

int daemon(int nochdir, int noclose);

兩個引數,第一個引數nochdir如果設定為0的話表示將工作目錄改為根目錄。第二個引數noclose如果設定為0的話就將檔案描述符重定向到/dev/null檔案。與上面原理相似。
所以,簡單的執行下面程式碼就可以建立一個守護程序。

#include<stdio.h>
#include<unistd.h>

int main()
{
     daemon(0,0);
     while(1);//死迴圈為了方便檢視
     return 0;
}

是不是感覺好坑啊?是很坑,但是上面的一大堆道理是必須要懂的!

拓展:fork一次和fork兩次?

我們利用第一種比較繁瑣的方式建立守護程序時,是利用父程序fork一次建立了子程序。有時編寫守護程序時需要fork兩次,關於這樣做的原因有以下兩種說法:

  1. 避免出現殭屍程序,程序有一種狀態叫做ZOMBIE,稱為殭屍狀態。通常是因為子程序異常或正常退出後沒有父程序(呼叫wait或wiatpid)來給它收屍,而導致子程序成為殭屍程序。雖然我們的做法是fork子程序後立即將父程序退出,子程序會被1號程序init接管,並不會出現殭屍程序。但是我們不能認為父程序僅僅是為了建立子程序而出現的,父程序也有他自己的事情要做。如果父程序因為執行某段程式而阻塞,而子程序已經政策結束或異常退出,此時子程序並沒有被init程序接收。子程序就變成了一個無人收屍的殭屍程序
  2. 避免開啟新的終端。我們知道,守護程序是沒有控制終端的。先說明一下,開啟終端的一個前提條件是該程序必須是會話組長。而我們fork一次並且setsid的兒子程序正好是會話組長。fork第二次,令孫子程序擔任守護程序。而且孫子子程序ID != 兒子程序ID(會話組長)。這樣就防止了守護程序再次開啟新的控制終端。

我本人更傾向於第二種說法,推薦大家第二種。

相關推薦

Linux程序關係守護程序

概念 守護程序也稱精靈程序,是執行在後臺的一種特殊程序。守護程序獨立於控制終端並且週期性的執行某種任務或者等待處理某些打算的事件。可認為守護程序目的就是防止終端產生的一些訊號讓程序退出 特點 所有的守護程序都沒有控制終端,其終端名(TTY)設定為問號(?

Linux程序關係守護程序

程序間關係 程序組/作業/會話 程序組 程序組是一個或多個程序的集合,通常它們與一組作業相關聯,可以接受來自同一終端的各種訊號。 每個程序除了有一個程序ID之外,還屬於一個程序組。 每個程序組都有唯一的程序組ID(整數,也可以存放在pid_t型別

Linux -- 程序關係守護程序

1, 程序組(Process Group) 每個程序除了有一個程序ID之外,還屬於一個程序組。 程序組是一個或多個程序的集合。 通常,它們與同一 作業 相關聯,可以接收來自同一終端的各種訊號。 每個程序組有一個唯一的程序組ID。每個程序組都可以有一個組長

Linux程序通訊管道通訊詳解

        在學習程序的時候,我們瞭解到了程序的獨立性:程序之間是相互獨立的,每個程序有自己的虛擬地址空間,並且虛擬地址空間通過頁表的對映,對映到屬於自己的實體記憶體上。並且各個程序之間互相不影響,執行自己的程式碼。    

linux 程序通訊FIFO

1.概述 FIFO與管道幾乎類似,所以FIFO也是一個位元組流,從FIFO讀取的順序也是與被寫入FIFO的順序一致,容量是也有限的,也是可以確保寫入不超過PIPE_BUF位元組的操作是原子的,FIFO的本質也是一個管道,但傳遞方向是可以雙向的,它們兩者之間的最大差別在於FIFO在檔案系統中擁有一個名稱,並且

程序通訊Linux C管道程式設計

管道簡述 管道(pipe)是Unix/Linux中最常見的程序間通訊方式之一,它在兩個程序之間實現一個數據流通的通道,資料以一種資料流的方式在程序間流動。在系統中,管道相當於檔案系統上的一個檔案,用於快取所要傳輸的資料。在某些特性上又不同於檔案,例如當資料讀出後,管道中就沒有資料了,但檔案沒

LinuxLinux程序通訊訊息佇列

1、訊息佇列概念引入    訊息佇列提供了一個從一個程序向另外一個程序傳送一塊資料的方法每個資料塊都被認為是有一個型別,接收者程序接收的資料塊可以有不同的型別值訊息佇列也有管道一樣的不足,就是每個訊息的最大長度是有上限的(MSG

Linux程序通訊訊號通訊

訊號通訊是Linux程序間通訊的一種方式。 1.什麼是訊號? 訊號是系統響應某些條件而產生的一個事件,接收到該訊號的程序會相應地採取一些措施。例如我們在windows系統中想強制結束一個程式我們需要用到的是工作管理員,而在Linux中,我們是通過訊號來實現的,執

Linux高階程式設計基礎——程序通訊匿名管道

程序間通訊之匿名管道 利用匿名管道實現父子程序間通訊,要求 父程序傳送字串“hello child”給子程序; 子程序收到父程序傳送的資料後,給父程序回覆“hello farther”; 父子程序通訊完畢,父程序依次列印子程序的退出狀態以及子程序的pid。

Linux高階程式設計基礎——程序通訊訊號的安裝與傳送

程序間通訊之訊號的安裝與傳送 呼叫setitimer函式分別觸發SIGALRM訊號,SIGVTALRM訊號,SIGPROF訊號 ;(可以由多程序分別觸發每個訊號) 編寫訊號安裝函式,在該函式內部能判斷接受到的是什麼訊號,並把訊號打印出來。 #include

Linux高階程式設計基礎——程序通訊用sigqueue函式和sigaction函式實現訊號的安裝與傳送

程序間通訊之用sigqueue函式和sigaction函式實現訊號的安裝與傳送 程序A向程序B傳送SIGUSR1訊號; 程序B收到訊號後,列印字串“receive SIGUSR1”; 要求用sigqueue函式和sigaction函式實現以上功能; /這個實

Linux高階程式設計基礎——程序通訊訊號值操作

程序間通訊之訊號值操作 程序A向程序B傳送訊號,該訊號的附帶資訊為一個值為20的整數; 程序B完成接收訊號的功能,並且打印出訊號名稱以及隨著訊號一起傳送過來的整形變數值。 /這個實驗分成兩個小部分。要把這兩個小程式分開執行,在執行這兩個程式之前先在終端上進入

程序通訊Linux C命名管道程式設計

命名管道 管道(匿名管道)的使用侷限性大,這與管道的實現機制有關。而命名管道(Named Pipe)不僅可在同一臺計算機的任意不同程序之間通訊,而且還可以在跨越一個網路的不同計算機的不同程序之間,支援可靠的、單向或雙向的資料通訊。 命名管道不同於管道之處在於它提供一個路徑

程序通訊Linux共享記憶體程式設計

共享記憶體 共享記憶體(Shared Memory)是指多個程序共享一段指定的記憶體空間進行資料互動,三種System V IPC機制(另外兩種是訊號量和訊息佇列)中共享記憶體是速度最快的一種。 共享記憶體機制是最快的一種程序間通訊(Interprocess Commun

Linux程序通訊訊息佇列、訊號量和共享儲存

訊息佇列、訊號量和共享儲存是IPC(程序間通訊)的三種形式,它們功能不同,但有相似之處,下面先介紹它們的相似點,然後再逐一說明。 1、相似點 每個核心中的IPC結構(訊息佇列、訊號量和共享儲存)都用一個非負整數的識別符號加以引用,與檔案描述符不同,當一個

Linux程序通訊pipe

1、管道(PIPE)        從概念上說,管道是兩個程序之間的一個connection,因此一個程序的標準輸出就變成了另一個程序的標準輸入。在Unix作業系統中,管道用於程序間通訊(inter-process communication). (1

Linux程序通訊POSIX訊息佇列

訊息佇列可認為是一個訊息連結串列,它允許程序之間以訊息的形式交換資料。有足夠寫許可權的程序或執行緒可往佇列中放置訊息,有足夠讀許可權的程序或執行緒可從佇列中取走訊息。每個訊息都是一個記錄,它由傳送者賦予一個優先順序。與管道不同,管道是位元組流模型,沒有訊息邊界。

Linux程序通訊——管道(整理)

 程序間通訊 fork pipe pie_t 等用法(管道機制 通訊) 每個程序各自有不同的使用者地址空間,任 何一個程序的全域性變數在另一個程序中都看不到,所以程序之間要交換資料必須通過核心,在核心中開闢一塊緩衝 區,程序1把資料從使用者空間拷到核心緩衝區,程序2再從

linux 程序通訊訊號

Linux訊號(signal) 機制分析 轉載至:https://www.cnblogs.com/hoys/archive/2012/08/19/2646377.html 【摘要】本文分析了Linux核心對於訊號的實現機制和應

Linux程序通訊POSIX共享記憶體

共享記憶體是最高效的IPC機制,因為它不涉及程序之間的任何資料傳輸。這種高效率帶來的問題是,我們必須用其他輔助手段來同步程序對共享記憶體的訪問,否則會產生競態條件。因此,共享記憶體通常和其他程序間通訊方式一起使用。 Linux下有三種共享記憶體的IPC技術:S