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,這樣的話,當庫函數試圖通過標準輸入輸出,標準錯誤時是沒有效果的。當一個守護進程脫離了終端時,就沒有地方打印信息;也沒有地方接收來自用戶的交互式輸入。甚至當一個守護進程從一個交互式的會話開始,守護進程在後臺運行,登陸會話關閉也不會影響到守護進程。如果其他用戶用同樣的終端登陸,我們不用設想從守護進程打印信息到終端,也別指望用戶讀取守護進程。

以上主要參考:http://www.cnblogs.com/iceocean/articles/1650475.html


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

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig)
    DESCRIPTION
    The  kill()  system  call can be used to send any signal to any process
    group or process.
    If pid is positive, then signal sig is sent to pid.
    If pid equals 0, then sig is sent to every process in the process group
    of the current process
    If pid equals -1, then sig is sent to every process for which the call-
    ing process has permission  to  send  signals,  except  for  process  1
    (init), but see below.
    If  pid  is less than -1, then sig is sent to every process in the pro-
    cess group -pid.
    If sig is 0, then no signal is sent, but error checking is  still  per-
    formed.
    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來查找],基本邏輯如下:

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


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

    /**
     * @Brief  write the pid into the szPidFile
     *
     * @Param szPidFile name of pid file
     */
void writePidFile(const char *szPidFile)
{
    /*open the file*/
    char            str[32];
    int lfp = open(szPidFile, O_WRONLY|O_CREAT|O_TRUNC, 0600);
    if (lfp < 0) exit(1);

    /*F_LOCK(block&lock) F_TLOCK(try&lock) F_ULOCK(unlock) F_TEST(will not lock)*/
    if (lockf(lfp, F_TLOCK, 0) < 0) {
        fprintf(stderr, "Can't Open Pid File: %s", szPidFile);
        exit(0);
    }

    /*get the pid,and write it to the pid file.*/
    sprintf(str, "%d\n", getpid()); // \n is a symbol.
    ssize_t len = strlen(str);
    ssize_t ret = write(lfp, str, len);
    if (ret != len ) {
        fprintf(stderr, "Can't Write Pid File: %s", szPidFile);
        exit(0);
    }
    close(lfp);
}


 

 

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