Linux系統編程——特殊進程之守護進程

來源:互聯網
上載者:User

標籤:linux   多任務編程   系統編程   守護進程   

什麼是守護進程?

守護進程(Daemon Process),也就是通常說的 Daemon 進程(精靈進程),是 Linux 中的後台服務進程。它是一個生存期較長的進程,通常獨立於控制終端並且周期性地執行某種任務或等待處理某些發生的事件。


守護進程是個特殊的孤兒進程,這種進程脫離終端,為什麼要脫離終端呢?之所以脫離於終端是為了避免進程被任何終端所產生的資訊所打斷,其在執行過程中的資訊也不在任何終端上顯示。由於在 Linux 中,每一個系統與使用者進行交流的介面稱為終端,每一個從此終端開始啟動並執行進程都會依附於這個終端,這個終端就稱為這些進程的控制終端,當控制終端被關閉時,相應的進程都會自動關閉。


Linux 的大多數伺服器就是用守護進程實現的。比如,網際網路服務器 inetd,Web 服務器 httpd 等。


如何查看守護進程

在終端敲:ps axj

  • a 表示不僅列目前使用者的進程,也列出所有其他使用者的進程
  • x 表示不僅列有控制終端的進程,也列出所有不控制終端的進程
  • j 表示列出與作業控制相關的資訊


從可以看出守護進行的一些特點:

  • 守護進程基本上都是以超級使用者啟動( UID 為 0 )
  • 沒有控制終端( TTY 為 ?)
  • 終端進程組 ID 為 -1 ( TPGID 表示終端進程組 ID)


一般情況下,守護進程可以通過以下方式啟動:

  • 在系統啟動時由啟動指令碼啟動,這些啟動指令碼通常放在 /etc/rc.d 目錄下;
  • 利用 inetd 超級伺服器啟動,如 telnet 等;
  • 由 cron 定時啟動以及在終端用 nohup 啟動的進程也是守護進程。

如何編寫守護進程?下面是編寫守護進程的基本過程:1) 屏蔽一些控制終端操作的訊號
這是為了防止守護進行在沒有運行起來前,控制終端受到幹擾退出或掛起。
signal(SIGTTOU,SIG_IGN); signal(SIGTTIN,SIG_IGN); signal(SIGTSTP,SIG_IGN); signal(SIGHUP ,SIG_IGN);

2) 在後台運行
這是為避免掛起控制終端將守護進程放入後台執行。方法是在進程中調用 fork() 使父進程終止, 讓守護進行在子進程中後台執行。 
if( pid = fork() ){ // 父進程exit(0);     //結束父進程,子進程繼續}

3) 脫離控制終端、登入工作階段和進程組
有必要先介紹一下 Linux 中的 進程與控制終端,登入工作階段和進程組之間的關係:進程屬於一個進程組,進程組號(GID)就是進程組長的進程號(PID)。登入工作階段可以包含多個進程組。這些進程組共用一個控制終端。這個控制終端通常是建立進程的 shell 登入終端。 控制終端、登入工作階段和進程組通常是從父進程繼承下來的。 我們的目的就是要擺脫它們 ,使之不受它們的影響。因此需要調用 setsid() 使子進程成為新的交談群組長,範例程式碼如下:
setsid();

setsid() 調用成功後,進程成為新的交談群組長和新的進程組長,並與原來的登入工作階段和進程組脫離。由於會話過程對控制終端的獨佔性,進程同時與控制終端脫離。 

4) 禁止進程重新開啟控制終端
現在,進程已經成為無終端的交談群組長, 但它可以重新申請開啟一個控制終端。可以通過使進程不再成為交談群組長來禁止進程重新開啟控制終端,採用的方法是再次建立一個子進程,範例程式碼如下:
if( pid=fork() ){ // 父進程exit(0);      // 結束第一子進程,第二子進程繼續(第二子進程不再是交談群組長) }

5) 關閉開啟的檔案描述符
進程從建立它的父進程那裡繼承了開啟的檔案描述符。如不關閉,將會浪費系統資源,造成進程所在的檔案系統無法卸下以及引起無法預料的錯誤。按如下方法關閉它們:
// NOFILE 為 <sys/param.h> 的宏定義// NOFILE 為檔案描述符最大個數,不同系統有不同限制for(i=0; i< NOFILE; ++i){// 關閉開啟的檔案描述符close(i);}

6) 改變當前工作目錄
進程活動時,其工作目錄所在的檔案系統不能卸下。一般需要將工作目錄改變到根目錄。對於需要轉儲核心,寫作業記錄的進程將工作目錄改變到特定目錄如 /tmp。範例程式碼如下:
chdir("/");

7) 重設檔案建立掩模
進程從建立它的父進程那裡繼承了檔案建立掩模。它可能修改守護進程所建立的檔案的存取許可權。為防止這一點,將檔案建立掩模清除:
umask(0);

8) 處理 SIGCHLD 訊號
但對於某些進程,特別是伺服器處理序往往在請求到來時產生子進程處理請求。如果父進程不等待子進程結束,子進程將成為殭屍進程(zombie)從而佔用系統資源(關於殭屍進程的更多詳情,請看《特殊進程之殭屍進程》)。如果父進程等待子進程結束,將增加父進程的負擔,影響伺服器處理序的並發效能。在 Linux 下可以簡單地將 SIGCHLD 訊號的操作設為 SIG_IGN 。
signal(SIGCHLD, SIG_IGN);

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

範例程式碼如下:
#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;}

運行結果如下:

本教程範例程式碼下載請點此處。
參考資料:《Linux進階程式設計》

Linux系統編程——特殊進程之守護進程

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.