1. 程式人生 > >Linux多工程式設計(七)---Linux守護程序及其基礎實驗

Linux多工程式設計(七)---Linux守護程序及其基礎實驗

守護程序概述

   守護程序,又叫daemon程序(不知怎的,我突然想起來吸血鬼日記中的達蒙了,很好看的美劇),是Linux中的後臺服務程序。他是一個生存期較長的程序,通常獨立於控制終端並且週期性地執行某種任務或者等待處理某些發生的事件。守護程序常常在系統引導載入時啟動,在系統關閉時終止。Linux有很多系統哦服務,大多數服務都是通過守護程序實現的。同時,守護程序還能完成許多系統任務,例如,作業規劃程序cronf、列印程序lqd等(這裡的結尾字母 d 就是 daemon的意思)。

   在Linux中,每一個系統與使用者進行交流的介面稱為終端,每一個從此終端開始執行的程序都會依附於這個終端,這個終端稱為這些程序的控制終端

,當控制終端關閉時,相應的程序都會自動關閉。但是守護程序卻能夠突破這種限制,它從被執行開始運轉,直到接收到某種訊號或者整個系統關閉時才退出。如果想讓某個程序不因為使用者、終端或者其它的變化而受到影響,那麼就必須把這個程序變成一個守護程序。可見,守護程序是非常重要的。

編寫守護程序步驟

   編寫守護程序遵循一個特定的流程,下面就說一下守護程序的建立步驟。

1、建立子程序,父程序退出。

   這是編寫守護程序的第一步。由於守護程序是脫離控制終端的,因此,完成第一步後就會在shell終端造成一種程式已經執行完畢的假象,之後的所有工作都在子程序中完成,而使用者在shell終端則可以執行其他的命令,從而在形式上做到與控制終端的脫離。

   但是,父程序建立了子程序後退出,此時該子程序不就沒有父程序了嗎?守護程序中確實會出現這麼一個有趣的現象:由於父程序已經先於子程序退出,就會造成子程序沒有父程序,從而變成一個孤兒程序。在Linux中,每當系統發現一個孤兒程序時,就會自動由1號程序(也就是 init 程序)收養它,這樣原先的子程序就會變成 init 程序的子程序。其關鍵程式碼如下;

   

2、在子程序中建立新會話   

   這個步驟是建立守護程序最重要的一步,雖然實現非常簡單,但意義卻非常重大。在這裡使用的是系統函式 setsid(),在具體介紹 setsid()之前,先了解以下兩個概念:程序組和會話期。

   ●  程序組。程序組是一個或多個程序的集合。程序組由程序組ID來唯一標識。除了程序號PID之外,程序組ID也是一個程序的必備屬性。每隔程序組都有一個組長程序,其組長程序的程序號PID等於程序組ID,且該程序組ID不會因為組長程序的退出而受到影響。(組長沒了,再找個組員來擔任組長唄)

   ●  會話期。會話組是一個或多個程序組的集合。通常,一個會話開始於使用者登入,終止於使用者退出,在此期間該使用者執行的所有程序都屬於這個會話期。程序組和會話期之間的關係如圖1所示:

    

   接下來具體介紹 setsid()的相關內容。

   ① setsid()函式的作用。setsid()函式用於建立一個新的會話組,並讓執行此函式的程序擔任該會話組的組長。呼叫setsid()有以下3個作用:

         ●   讓程序擺脫原會話的控制

         ●   讓程序擺脫原程序組的控制

         ●   讓程序擺脫原控制終端的控制

    那麼,回過頭來想想,在建立守護程序時為什麼要呼叫 setsid()函式呢?是這樣的,在建立守護程序的第一步中,呼叫了fork()函式建立子程序再令父程序退出。由於在呼叫 fork()函式時,子程序全盤複製了父程序的會話期、程序組和控制終端等,雖然父程序退出了,但原先的會話期、程序組和控制終端等並沒有改變,因此,還不是真正意義上的獨立。而setsid()函式能夠使程序完全獨立出來,從而脫離所有其他程序的控制。

    ② setsid函式格式

   

3、改變當前目錄為根目錄

   這一步也是必要的步驟。使用fork()建立的子程序繼承了父程序的當前工作目錄。由於在程序執行過程中,當前目錄所在的檔案系統(如“/mnt/usb”等)是不能解除安裝的,這對以後的使用會造成諸多的麻煩(如系統由於某種原因需要進入單使用者模式)。因此,通常的做法是讓“/”作為守護程序的當前工作目錄,這樣就可以避免上述問題。當然,如有特殊需要,也可以把當前工作目錄換成其他的路徑,如/tmp。改變工作目錄的常見函式是chdir()。

4、重設檔案許可權掩碼

  檔案許可權掩碼是指遮蔽掉檔案許可權中的對應位。例如,有一個檔案許可權掩碼是050,它就遮蔽了檔案組擁有者的可讀與可執行許可權。由於使用fork()函式新建的子程序繼承了父程序的檔案許可權掩碼,這就給該子程序使用檔案帶來了諸多的麻煩。因此,把檔案許可權掩碼設定為0,可以大大增強該守護程序的靈活性。設定檔案許可權掩碼的函式是umask()。通常的使用方法為umask(0)。

5、關閉檔案描述符

   同文件許可權掩碼一樣,用fork()函式新建的子程序會從父程序那裡繼承一些已經開啟的檔案。這些被開啟的檔案可能永遠不會被守護程序讀或寫,但它們一樣消耗系統資源,而且可能導致所在的檔案系統無法被解除安裝。

   事實上,在上面的第2步之後,守護程序已經與所屬的控制終端失去了聯絡,因此,從終端輸入的字元不可能達到守護程序,守護程序中用常規方法(如printf())輸出的字元也不可能在終端上顯示出來。所以檔案描述符為0,1和2的3個檔案(常說的輸入/輸出和報錯這3個檔案)已經失去了存在的價值,也應該被關閉。通常了,按如下方式關閉檔案描述符:

  

  有關getdtables()的作用請看部落格:

  到這裡,一個簡單的守護程序就建立起來了。建立守護程序的流程圖如圖所示:

  

基礎實驗

   本實驗按照以上的建立流程建立了一個守護程序,然後讓守護程序每隔10s向日志文件/home/song/tmp/daemon.log   寫入一句話。程式程式碼如下,我也上傳到網站,點此下載

   

    

   我們先看一下 /tmp資料夾下是沒有daemon.log的

   

   下載檔案後,使用命令編譯:gcc dameon.c -o daemon

    然後執行命令: ./daemon  你可以看到此時沒有看到有什麼變化

    

    使用命令:ps -ef|grep ./daemon 利用ps中的關鍵字來檢視系統當前正在執行的程序中,有沒有咱們的daemon程序

    

    可以看到咱們的守護程序已經在運行了,再來看看/tmp目錄下的內容

        

    可以看到,已經有daemon.log日誌檔案了。

    然後使用命令:tail -f /tmp/daemonl.log  ,可以看到該程式每隔10s就會在對應的檔案中輸入相關的內容

   

   到這裡,這個實驗就已經結束了,通過前邊使用命令:ps -ef|grep ./daemon可以看到咱們這個程序的程序號是3346,現在使用命令:kill -9  3346將這個程序殺死,同時也把/tmp中的daemon.log檔案頁刪除,方便咱們下邊的實驗。

   

守護程序的出錯處理

   在編寫守護程序的具體除錯過程中會發現,由於守護程序完全脫離了控制終端,因此,不能像其他普通程序一樣,將錯誤資訊輸出到控制終端來通知程式設計師,即使使用gdb也無法正常除錯。那麼,守護程序的程序要如何除錯呢?一種通用的方法是使用 syslog 服務,將程式中的出錯資訊輸入到系統日誌檔案中(如“/var/log/messages”),從而可以直觀地看到程式的問題所在(“/var/log/message”系統日誌檔案只能由擁有root許可權的超級使用者檢視。在不同的Linux發行版本中,系統日誌檔案路徑全名可能有所不同,例如,我的ubuntu中路徑就是“/var/log/syslog”)。

    syslog 是Linux中的系統日誌管理服務,通過守護程序 syslogd 來維護。該守護程序在啟動時會讀一個配置檔案“/etc/syslog.conf”,該檔案決定了不同種類的訊息會發送到何處。例如,緊急訊息可被送到系統管理員並在控制檯上顯示,而警告訊息則可被記錄到一個檔案中。

    該機制提供了3個syslog相關函式,分別為 openlog()、syslog()和closelog(),下面就分別介紹這3個函式。

函式說明

    openlog()函式用於開啟系統日誌服務的一個連結;syslog()函式用於向日志文件中寫入資訊,在這裡可以設定訊息的優先順序、訊息輸出格式等;closelog()函式用於關閉系統日誌服務的連結。

函式格式

     

    

    

基礎實驗

      

      

      咱們可以嘗試用普通身份執行程式(redhat中不要用root,ubuntu正常執行就可以)。由於這裡的open()函式必須具有root許可權,因此,syslog 會將錯誤資訊寫入到系統日誌檔案("如/var/log/syslog")中,結果如下圖

     

    本實驗檔案syslog_damen.c點此下載