天天看點

Linux守護程序的程式設計方法

守護程序(Daemon)是運作在背景的一種特殊程序。它獨立于控制終端并且周期性地執行某種任務或等待處理某些發生的事件。守護程序是一種很有用的程序。Linux的大多數伺服器就是用守護程序實作的。比如,Internet伺服器inetd,Web伺服器httpd等。同時,守護程序完成許多系統任務。比如,作業規劃程序crond,列印程序lpd等。 

守護程序的程式設計本身并不複雜,複雜的是各種版本的Unix的實作機制不盡相同,造成不同Unix環境下守護程序的程式設計規則并不一緻。這需要讀者注意,照搬某些書上的規則(特别是BSD4.3和低版本的System V)到Linux會出現錯誤的。下面将全面介紹Linux下守護程序的程式設計要點并給出詳細執行個體。 

一. 守護程序及其特性 

守護程序最重要的特性是背景運作。在這一點上DOS下的常駐記憶體程式TSR與之相似。其次,守護程序必須與其運作前的環境隔離開來。這些環境包括未關閉的檔案描述符,控制終端,會話和程序組,工作目錄以及檔案建立掩模等。這些環境通常是守護程序從執行它的父程序(特别是shell)中繼承下來的。最後,守護程序的啟動方式有其特殊之處。它可以在Linux系統啟動時從啟動腳本/etc/rc.d中啟動,可以由作業規劃程序crond啟動,還可以由使用者終端(通常是shell)執行。

總之,除開這些特殊性以外,守護程序與普通程序基本上沒有什麼差別。是以,編寫守護程序實際上是把一個普通程序按照上述的守護程序的特性改造成為守護程序。如果讀者對程序有比較深入的認識就更容易了解和程式設計了。 

二. 守護程序的程式設計要點 

前面講過,不同Unix環境下守護程序的程式設計規則并不一緻。所幸的是守護程序的程式設計原則其實都一樣,差別在于具體的實作細節不同。這個原則就是要滿足守護程序的特性。同時,Linux是基于Syetem V的SVR4并遵循Posix标準,實作起來與BSD4相比更友善。程式設計要點如下; 

1. 屏蔽一些有關控制終端操作的信号。

這是為了防止在守護程序沒有正常運轉起來時,控制終端受到幹擾退出或挂起。示例如下:

 所有的信号都有自己的名字。這些名字都以“SIG”開頭,隻是後面有所不同。開發人員可以通過這些名字了解到系統中發生了什麼事。當信号出現時,開發人員可以要求系統進行以下三種操作:

 忽略信号。大多數信号都是采取這種方式進行處理的,這裡就采用了這種用法。但值得注意的是對SIGKILL和SIGSTOP信号不能做忽略處理。

捕捉信号。最常見的情況就是,如果捕捉到SIGCHID信号,則表示子程序已經終止。然後可在此信号的捕捉函數中調用waitpid()函數取得該子程序的程序ID和它的終止狀态。另外,如果程序建立了臨時檔案,那麼就要為程序終止信号SIGTERM編寫一個信号捕捉函數來清除這些臨時檔案。

執行系統的預設動作。對絕大多數信号而言,系統的預設動作都是終止該程序。對這些有關終端的信号,一般采用忽略處理,進而保障了終端免受幹擾。

這類信号分别是,SIGTTOU(表示背景程序寫控制終端)、SIGTTIN(表示背景程序讀控制終端)、SIGTSTP(表示終端挂起)和SIGHUP(程序組長退出時向所有會議成員發出的)。

2. 在背景運作。 

為避免挂起控制終端将Daemon放入背景執行。方法是在程序中調用fork使父程序終止,讓Daemon在子程序中背景執行。 

if(pid=fork()) 

exit(0);//是父程序,結束父程序,子程序繼續 

3. 脫離控制終端,登入會話和程序組 

有必要先介紹一下Linux中的程序與控制終端,登入會話和程序組之間的關系:程序屬于一個程序組,程序組号(GID)就是程序組長的程序号(PID)。登入會話可以包含多個程序組。這些程序組共享一個控制終端。這個控制終端通常是建立程序的登入終端。 

控制終端,登入會話和程序組通常是從父程序繼承下來的。我們的目的就是要擺脫它們,使之不受它們的影響。方法是在第1點的基礎上,調用setsid()使程序成為會話組長: 

setsid(); 

說明:當程序是會話組長時setsid()調用失敗。但第一點已經保證程序不是會話組長。setsid()調用成功後,程序成為新的會話組長和新的程序組長,并與原來的登入會話和程序組脫離。由于會話過程對控制終端的獨占性,程序同時與控制終端脫離。 

4. 禁止程序重新打開控制終端 

現在,程序已經成為無終端的會話組長。但它可以重新申請打開一個控制終端。可以通過使程序不再成為會話組長來禁止程序重新打開控制終端: 

exit(0);//結束第一子程序,第二子程序繼續(第二子程序不再是會話組長) 

5. 關閉打開的檔案描述符 

程序從建立它的父程序那裡繼承了打開的檔案描述符。如不關閉,将會浪費系統資源,造成程序所在的檔案系統無法卸下以及引起無法預料的錯誤。按如下方法關閉它們(NOFILE在頭檔案中定義): 

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

close(i);

6. 改變目前工作目錄 

程序活動時,其工作目錄所在的檔案系統不能卸下。一般需要将工作目錄改變到根目錄。對于需要轉儲核心,寫運作日志的程序将工作目錄改變到特定目錄如/tmp:

chdir("/tmp") 

7. 重設檔案建立掩模 

程序從建立它的父程序那裡繼承了檔案建立掩模。它可能修改守護程序所建立的檔案的存取位。為防止這一點,将檔案建立掩模清除:

umask(0); 

8. 處理SIGCHLD信号 

處理SIGCHLD信号并不是必須的。但對于某些程序,特别是伺服器程序往往在請求到來時生成子程序處理請求。如果父程序不等待子程序結束,子程序将成為僵屍程序(zombie)進而占用系統資源。如果父程序等待子程序結束,将增加父程序的負擔,影響伺服器程序的并發性能。在Linux下可以簡單地将 SIGCHLD信号的操作設為SIG_IGN。 

signal(SIGCHLD,SIG_IGN); 

這樣,核心在子程序結束時不會産生僵屍程序。這一點與BSD4不同,BSD4下必須顯式等待子程序結束才能釋放僵屍程序。

繼續閱讀