1. 程式人生 > >linux 程序(關於守護程序、檢查一個程序是否活著、如何寫一個程序號檔案)

linux 程序(關於守護程序、檢查一個程序是否活著、如何寫一個程序號檔案)

本文主要包括三個部分:
    一是如何實現一個守護程序,二是如何檢測一個程序是否活著,三是保證某一執行檔案只有一個例項在執行。

/*
 * 1.守護程序
 */

守護程序的最大特點就是脫離了中斷,Linux提供了一個系統呼叫daemon(),要想自定義實現的話,主要包括以下六個步驟:

1.第一步是使用umask函式,把所有的檔案遮蔽字置0。檔案遮蔽字是可以繼承的,當你有相關操作時,如果你要建立一個檔案,繼承過來的遮蔽字可能阻止你建立相關屬性的檔案。比如:如果你明確的建立一個檔案為組可讀,組可寫。如果你沒有把遮蔽字清零,那麼繼承過來的遮蔽字可能不允許你新增這兩個屬性。

2.第二步,建立一個子程序,並且令父程序退出。這樣做有以下幾個好處:一,如果守護程序是一個簡單的shell命令啟動的,那麼父程序的終止可以使shell認為這個命令已經執行結束了。二,子程序繼承了父程序的組ID,但又有自己的程序ID,所以我們可以保證目前的子程序不是程序組長。這一步也是我們接下來要用到的setid函式之前的必要條件。

3.使用setsid函式建立一個新的對會話。首先,該程序變為一個新的會話組的會話頭。其次,成為了新的程序組的組長。最後該程序不再控制終端。在system V 下,一些人建議在此時重新fork一次,並且令父程序退出。第二個子程序仍然是一個守護程序。這樣做可以保證當前程序不是一個會話組的組長,這樣就可以防止他獲得控制終端的能力。作為選擇,為了防止獲得終端的控制權,確定開啟終端驅動時明確設定O_NOCTTY。

4.把當前工作目錄變為根目錄。當前的工作目錄是繼承父程序的。守護程序是一直存在的,除非你重啟計算機。如果你的守護程序是掛載到檔案系統上的,那這個檔案系統就不能解除安裝掉。

5.不需要的檔案描述符應當關掉。這樣可以防止守護程序持有從父程序繼承過來的檔案描述符。我們可以獲取最大的檔案描述符,或者使用getrlimit函式來決定最大的檔案描述符的值。並且全部關閉。(非必要)

6.一些守護程序把0,1,2這三個檔案描述符指向/dev/null,這樣的話,當庫函式試圖通過標準輸入輸出,標準錯誤時是沒有效果的。當一個守護程序脫離了終端時,就沒有地方列印資訊;也沒有地方接收來自使用者的互動式輸入。甚至當一個守護程序從一個互動式的會話開始,守護程序在後臺執行,登陸會話關閉也不會影響到守護程序。如果其他使用者用同樣的終端登陸,我們不用設想從守護程序列印資訊到終端,也別指望使用者讀取守護程序。


/*
 * 2.如何檢查一個程序是否活著
 */
    判斷一個程序是否活著,我們主要是通過kill這一系統呼叫來完成,先看一下kill的manual page:

  1. #include 
    <sys/types.h>
  2. #include <signal.h>
  3. int kill(pid_t pid, int sig)  
  4.     DESCRIPTION  
  5.     The  kill()  system  call can be used to send any signal to any process  
  6.     group or process.  
  7.     If pid is positive, then signal sig is sent to pid.  
  8.     If pid equals 0, then sig is sent to every process in the process group  
  9.     of the current process  
  10.     If pid equals -1, then sig is sent to every process for which the call-  
  11.     ing process has permission  to  send  signals,  except  for  process  1  
  12.     (init), but see below.  
  13.     If  pid  is less than -1, then sig is sent to every process in the pro-  
  14.     cess group -pid.  
  15.     If sig is 0, then no signal is sent, but error checking is  still  per-  
  16.     formed.  
  17.     if you can send a signal to PID, and returns 1 if you can't (don't have access or invalid PID)。  

    
    所以kill(pid,0)可以用於檢測一個為pid的程序是否還活著[在shell下面可以用ps來查詢],基本邏輯如下:

  1. if(kill(pid,0)!=0)  
  2.     it's dead.  
  3. else
  4.     it's alive.  


/*
 *3.保證某一執行檔案只有一個例項在執行
 */
    這樣的需求主要是解決保證只有同時只有一個這樣的程序在執行,像mysql都這樣處理:
    1.啟動程序後,先檢查pid檔案是否存在,存在則讀取之前寫入的pid,然後用上面的kill(pid,0);來檢查是否活著,
    2.活著則退出程序,不允許再啟動一個程序,否則啟動並將當前的pid寫入pid檔案。寫入的時候要鎖住檔案,避免
    其他程序也往裡面寫,主要是lockf這個系統呼叫,方法是:

  1. /** 
  2.      * @Brief  write the pid into the szPidFile 
  3.      * 
  4.      * @Param szPidFile name of pid file 
  5.      */
  6. void writePidFile(constchar *szPidFile)  
  7. {  
  8.     /*open the file*/
  9.     char            str[32];  
  10.     int lfp = open(szPidFile, O_WRONLY|O_CREAT|O_TRUNC, 0600);  
  11.     if (lfp < 0) exit(1);  
  12.     /*F_LOCK(block&lock) F_TLOCK(try&lock) F_ULOCK(unlock) F_TEST(will not lock)*/
  13.     if (lockf(lfp, F_TLOCK, 0) < 0) {  
  14.         fprintf(stderr, "Can't Open Pid File: %s", szPidFile);  
  15.         exit(0);  
  16.     }  
  17.     /*get the pid,and write it to the pid file.*/
  18.     sprintf(str, "%d\n", getpid()); // \n is a symbol.
  19.     ssize_t len = strlen(str);  
  20.     ssize_t ret = write(lfp, str, len);  
  21.     if (ret != len ) {  
  22.         fprintf(stderr, "Can't Write Pid File: %s", szPidFile);  
  23.         exit(0);  
  24.     }  
  25.     close(lfp);  
  26. }