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:
-
#include
- #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(constchar *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);
- }