《UNIX環境高級編程》第13章 守護進程

13.1 引言

守護進程(daemon)是生存期長的一種進程。它們常常在系統引導裝入時啓動,僅在系統關閉時才終止(生存期)。因爲它們沒有控制終端,所以說它們時在後頭運行的。UNIX系統有很多守護進程,它們執行日常事務活動。
本章將說明守護進程的結構,以及如何編寫守護進程程序。因爲守護進程沒有控制終端,我們需要了解在出現問題時,daemon如何報告出錯情況。

13.2守護進程的特徵

先來看看一些常用的系統守護進程,以及它們是怎樣和進程組、控制終端和會話這三個概論相關聯的。

ps -axj
  • -a顯示由其他用戶所擁有的進程狀態(UNIX爲多用戶系統)
  • -x顯示沒有控制終端的進程;
  • -j顯示與作業有關的信息

PPID爲0的進程通常是內核進程,它們作爲系統引導裝入過程的一部分而啓動。(init是一個例外,它是一個由內核在引導裝入時啓動的用戶層次的命令)內核進程是特殊的,通常存在於系統的整個生命週期中。它們以超級用戶特權運行,無控制終端、無命令行。
在ps輸出實例中,內核守護進程的名字出現在方框中,很多形式爲’k*d’的進程其中‘k’指kernel,‘d’指daemon
該版本的linux使用一個名爲kthreadd的特殊內核進程類創建其他內核進程,所以kthreadd表現爲其他內核進程的父進程。對於需要在進程上下文執行工作但卻不被用戶層進程上下文調用的每一個內核組件,通常有它自己的內核守護進程。 例如在linux中:

  • kswapd守護進程也稱爲內存換頁守護進程。它支持虛擬內存子系統在經過一段時間後將髒頁面慢慢地寫回磁盤來回收這些頁面
  • flush守護進程在可用內存達到設置的最小閾值時將髒頁面沖洗至磁盤。多個沖洗守護進程可以同時存在,每個寫回設備都有一個沖洗守護進程。例如flush-8:0的沖洗守護進程。從名字可以看出是通過主設備號8和副設備號0來識別的。
  • sync_supers守護進程定期將文件系統元數據沖洗至磁盤。
  • jbd守護進程幫助實現了ext4文件系統中的日誌功能。
  • rpcbind守護進程提供將遠程過程調用(remote procedure call,RPC)程序號映射爲網絡端口號的服務。
  • inetd守護進程,它偵聽系統網絡接口,以便取得來自網絡的對各種網絡服務進程的請求。
  • cron守護進程在定期安排的日期和時間執行命令。許多系統管理任務是通過cron每隔一段固定時間就運行相關程序得以實現的。
  • atd守護進程與cron類似。它允許用戶在指定的時間執行任務,但是每個任務它只執行一次,而非在定期安排的時間反覆執行。
  • cupsd守護進程是格打印假脫機進程,它處理系統提出的各個打印請求。
  • sshd守護進程提供了安全的遠程登錄和執行設施。
    注意:大多數守護進程都以超級用戶(root)權限運行。所有守護進程都沒有控制終端,其終端名設置爲問號。用戶層守護進程的父進程是init進程。

13.3 編程規則

在編寫守護進程程序時要遵循一些基本規則,以防止產生不必要的交換作用。

  1. 首先要做的是調用umask將文件模式創建屏蔽字(文件權限用) 設置爲一個已知的值(通常是0)。由繼承得來的文件模式創建屏蔽字可能會被設置爲拒絕某些權限。
  2. 調用fork,然後使父進程exit。這樣做實現了下面幾點。第一,如果該守護進程是作爲一條簡單的shell命令啓動的,那麼父進程終止會讓shell認爲這條命令已經執行完畢。第二,雖然子進程繼承了父進程的進程組ID,但獲得了一個新的進程ID,這就保證了子進程不是一個進程組的組長進程。這是下面將要進行的setsid調用的先決條件。
  3. 調用setsid創建一個會話。然後使調用進程稱爲新會話的首進程,稱爲一個新進程組的組長進程,沒有控制終端。
  4. 將當前工作目錄更改爲根目錄。從父進程繼承過來的當前工作目錄可能在一個掛載的文件系統中。因爲守護進程一直存在,那麼該文件系統就不能被卸載。
  5. 關閉不再需要的文件描述符。
  6. 某些守護進程打開/dev/null使其具有文件描述符0、1和2,這樣任何一個試圖使用標準輸入輸出的庫例程都不會產生任何效果。

13.4 出錯記錄

守護進程存在的一個問題是如何處理出錯消息。因爲它本身就不應該有控制終端,所以不能只是簡單地寫到標準出錯上。我們不希望守護進程使用控制檯設備,因爲很多工作站的控制檯設備都運行着一個窗口系統,也不會希望每個守護進程都將出錯消息寫到單獨的文件中,管理這些文件很費事。所以需要一個集中的守護進程出錯記錄設施
BSD的syslog設施得到了廣泛的應用,大多數守護進程都使用這一設施。
這裏寫圖片描述
有一下3種產生日誌消息的方法:

  1. 內核例程可以調用log函數。任何一個用戶進程都可以通過打開(open)並讀取(read)/dev/klog設備來讀取這些消息。這裏不涉及內核例程,就不進一步說明了。
  2. 大多數用戶進程(僅指守護進程)調用syslog函數來產生日誌消息。這使消息被髮送至UNIX域數據報套接字/dev/log.
  3. 無論一個用戶進程是在此主機上,還是通過TCP/IP網絡連接的此主機上的其他主機上,都可以講日誌消息發向UDP端口514.
    通常,syslogd守護進程讀取所有3種格式的日誌消息。此守護進程在啓動時讀一個配置文件,其文件名一般爲/etc/syslog.conf,該文件決定了不同種類的消息應送向何處。如:緊急消息可發送至系統管理員(若已經登錄),並在控制檯上打印,而警告消息則可以記錄到一個文件中。

守護進程記錄日誌消息的實施是/dev/log,其接口是syslog函數:

#include <syslog.h>
void openlog(const char *ident,int optipn,int facility);
void syslog(int priority,const char *format,...);
void closelog(void);
int setlogmask(int maskpri);//用於設置進程的記錄優先級屏蔽字。

除了syslog,很多平臺提供一種變體來處理可變參數列表。

#include <syslog.h>
#include <stdarg.h>
void vsyslog(int priority,const char *format,va_list arg);

大多數syslog實現將消息短時間處於隊列中。如果在此段時間中有重複消息達到,那麼syslog守護進程不會把它記錄到日誌記錄中,而是會打印輸出一條類似“上一條消息重複了N此”的消息。

13.5 單實例守護進程

爲了正常運作,某些守護進程(不是所有的都必須)會實現爲:在任意時刻只運行該守護進程的一個副本。
文件和記錄鎖機制爲這一種方法提供了基礎,該方法保障一個守護進程只有一個副本在運行。如果每一個守護進程創建一個固定名字的文件,並在該文件的整體上加一把鎖,那麼只允許創建一把這樣的鎖。在此後創建寫鎖的嘗試都會失敗,這向後續守護進程副本指明已有一個副本正在運行。

13.6 守護進程的慣例

在UNIX系統中,守護進程遵循下列通用慣例:

  • 若守護進程使用鎖文件,那麼該文件通常存儲在/var/run目錄中。然而需要注意的是,守護進程可能需要具有超級用戶權限才能在此目錄下創建文件。鎖文件的名字通常是name.pid。
  • 若守護進程支持配置選項,那麼配置文件通常存放在/etc目錄中。配置文件的名字通常是name.conf。
  • 守護進程可以用命令行啓動,但通常他們是由系統初始化腳本之一(/etc/rc*/etc/init.d/*)啓動的。如果在守護進程終止時,應當自動地重新啓動它,則我們可以在/etc/inittab(爲init進程的配置文件)中爲該守護進程包括respawn記錄項,這樣init就將重新啓動該守護進程。
  • 若一個守護進程有一個配置文件,那麼該守護進程啓動時會讀該文件,但在此之後一般就不會再查看它。若某個管理員更改了配置文件,那麼該守護進程可能需要配停止,然後再啓動,以使配置文件的更改生效。爲了避免此種麻煩,某些守護進程將捕捉SIGHUP信號,當他們接受到該信號時,重新讀配置文件。

13.7 客戶進程-服務器進程模型

守護進程常常用作服務器進程。確實,我們可以稱syslogd進程爲服務器進程,用戶進程(客戶進程)用UNIX域數據報套接字向其發送消息。
一般而言,服務器進程等待客戶進程與其聯繫,提出某種服務類型的請求。例如,syslogd服務器進程提供的服務是將一條出錯消息記錄到日誌文件中。


在服務器進程中調用fork然後exec另一個程序來向客戶進程提供服務是很常見的。這些服務器進程通常管理者多個文件描述符:通信端點、配置文件、日誌文件和類似的文件。

13.8 小結

在大多數UNIX系統中,守護進程是一直運行的。
爲了初始化我們自己的進程,使之作爲守護進程運行,需要謹慎的思索並理解第9章所說明的進程之間的關係。
因爲守護進程沒有控制終端,本章還說明了守護進程記錄出錯消息的幾種方法。
還討論了守護進程遵循的若干慣例,給出了幾個如何實現慣例的實例。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章