Daemon進程

這又是一個有趣的概念,daemon在英語中是"精靈"的意思,就像咱們常常在迪斯尼動畫裏見到的那些,有些會飛,有些不會,常常圍着動畫片的主人公轉來轉去,囉裏囉唆地提一些忠告,時不時倒黴地撞在柱子上,有時候還會想出一些小小的花招,把主人公從敵人手中救出來,正因如此,daemon有時也被譯做"守護神"。因此,daemon進程在國內也有兩種譯法,有些人譯做"精靈進程",有些人譯做"守護進程",這兩種稱呼的出現頻率都很高。shell

與真正的daemon類似,daemon進程也習慣於把本身隱藏在人們的視線以外,默默爲系統作出貢獻,有時人們也把它們稱做"後臺服務進程"。daemon進程的壽命很長,通常來講,從它們一被執行開始,直到整個系統關閉,它們纔會退出。幾乎全部的服務器程序,包括咱們熟知的Apache和wu-FTP,都用daemon進程的形式實現。不少Linux下常見的命令如inetd和ftpd,末尾的字母d就是指daemon。服務器

爲何必定要使用daemon進程呢?Linux中每個系統與用戶進行交流的界面稱爲終端(terminal),每個今後終端開始運行的進程都會依附於這個終端,這個終端就稱爲這些進程的控制終端(Controlling terminal),當控制終端被關閉時,相應的進程都會被自動關閉。關於這點,讀者能夠用X-Window中的XTerm試驗一下,(每個XTerm就是一個打開的終端,)咱們能夠經過鍵入命令啓動應用程序,好比:session

$netscape動畫

而後咱們關閉XTerm窗口,剛剛啓動的netscape窗口也會隨之一同忽然蒸發。可是daemon進程卻可以突破這種限制,即便對應的終端關閉,它也能在系統中長久地存在下去,若是咱們想讓某個進程長命百歲,不由於用戶或終端或其餘的變化而受到影響,就必須把這個進程變成一個daemon進程。命令行

若是想把本身的進程變成daemon進程,咱們必須嚴格按照如下步驟進行:繼承

  1. 調用fork產生一個子進程,同時父進程退出。咱們全部後續工做都在子進程中完成。這樣作咱們能夠:
    1. 若是咱們是從命令行執行的該程序,這能夠形成程序執行完畢的假象,shell會回去等待下一條命令;
    2. 剛剛經過fork產生的新進程必定不會是一個進程組的組長,這爲第2步的執行提供了前提保障。

    這樣作還會出現一種頗有趣的現象:因爲父進程已經先於子進程退出,會形成子進程沒有父進程,變成一個孤兒進程(orphan)。每當系統發現一個孤兒進程,就會自動由1號進程收養它,這樣,原先的子進程就會變成1號進程的子進程。 
  2. 調用setsid系統調用。這是整個過程當中最重要的一步。setsid的介紹見附錄2,它的做用是建立一個新的會話(session),並自任該會話的組長(session leader)。若是調用進程是一個進程組的組長,調用就會失敗,但這已經在第1步獲得了保證。調用setsid有3個做用:把當前工做目錄切換到根目錄。若是咱們是在一個臨時加載的文件系統上執行這個進程的,好比:/mnt/floppy/,該進程的當前工做目錄就會是/mnt/floppy/。在整個進程運行期間該文件系統都沒法被卸下(umount),而不管咱們是否在使用這個文件系統,這會給咱們帶來不少不便。解決的方法是使用chdir系統調用把當前工做目錄變爲根目錄,應該不會有人想把根目錄卸下吧。關於chdir的用法,參見附錄1。 
    1. 讓進程擺脫原會話的控制;
    2. 讓進程擺脫原進程組的控制;
    3. 讓進程擺脫原控制終端的控制;

    總之,就是讓調用進程徹底獨立出來,脫離全部其餘進程的控制。 
  3. 固然,在這一步裏,若是有特殊的須要,咱們也能夠把當前工做目錄換成其餘的路徑,好比/tmp。 
  4. 將文件權限掩碼設爲0。這須要調用系統調用umask,參見附錄3。每一個進程都會從父進程那裏繼承一個文件權限掩碼,當建立新文件時,這個掩碼被用於設定文件的默認訪問權限,屏蔽掉某些權限,如通常用戶的寫權限。當另外一個進程用exec調用咱們編寫的daemon程序時,因爲咱們不知道那個進程的文件權限掩碼是什麼,這樣在咱們建立新文件時,就會帶來一些麻煩。因此,咱們應該從新設置文件權限掩碼,咱們能夠設成任何咱們想要的值,但通常狀況下,你們都把它設爲0,這樣,它就不會屏蔽用戶的任何操做。 
    若是你的應用程序根本就不涉及建立新文件或是文件訪問權限的設定,你也徹底能夠把文件權限掩碼一腳踢開,跳過這一步。
  5. 關閉全部不須要的文件。同文件權限掩碼同樣,咱們的新進程會從父進程那裏繼承一些已經打開了的文件。這些被打開的文件可能永遠不被咱們的daemon進程讀或寫,但它們同樣消耗系統資源,並且可能致使所在的文件系統沒法卸下。須要指出的是,文件描述符爲0、1和2的三個文件(文件描述符的概念將在下一章介紹),也就是咱們常說的輸入、輸出和報錯這三個文件也須要被關閉。極可能很多讀者會對此感到奇怪,難道咱們不須要輸入輸出嗎?但事實是,在上面的第2步後,咱們的daemon進程已經與所屬的控制終端失去了聯繫,咱們從終端輸入的字符不可能達到daemon進程,daemon進程用常規的方法(如printf)輸出的字符也不可能在咱們的終端上顯示出來。因此這三個文件已經失去了存在的價值,也應該被關閉。 

下面,就然咱們親眼看一個daemon進程的誕生:進程

/* daemon.c */資源

#include<unistd.h>terminal

#include<sys/types.h>it

#include <sys/stat.h>

#define MAXFILE 65535

main()

  pid_t pid;  int i;        

  pid=fork(); 

  if(pid<0)

  {  

    printf("error in fork\n");  

    exit(1); 

  }

  else if(pid>0)   

  /* 父進程退出 */  

  exit(0);  

 

  /* 調用setsid */        

  setsid(); 

 

  /* 切換當前目錄 */        

  chdir("/"); 

 

  /* 設置文件權限掩碼 */ 

  umask(0); 

 

  /* 關閉全部可能打開的不須要的文件 */ 

  for(i=0;i<MAXFILE;i++)  

  close(i); 

 

  /*到如今爲止,進程已經成爲一個徹底的daemon進程,你能夠在這裏添加任何你要daemon作的事情*/  

  for(;;)  

  sleep(10);

}

相關文章
相關標籤/搜索