今天補了一下apue的13章,守護程序部分。這裡簡單記錄一下,當個筆記吧。
我的理解:守護程序就是後台程序,沒有控制終端,所以無法與使用者進行互動,就只是在後台默默執行。
使用umask
將檔案模式建立遮蔽字設定為乙個已知值。因為守護程序是乙個子程序,他會繼承父程序的檔案模式建立遮蔽字,所以本身它是乙個不確定的值。我們需要為守護程序設定合適的值。檔案模式建立遮蔽字用於決定建立新檔案或新目錄時,新檔案或新目錄的預設訪問許可權。
需要呼叫fork
,來產生子程序,然後父程序呼叫exit
結束。為什麼需要建立子程序呢,原因有兩個:
(1)守護程序本來就是由一條shell命令啟動的,父程序負責對這條命令的執行做準備,然後返回,讓shell認為這條命令執行完畢,接著準備讀取下一條命令;
(2)因為守護程序需要乙個新的會話,在這個會話中沒有控制終端,所以後面必須要使用setsid
來建立乙個新的會話,並將呼叫程序放入到這個新會話中。setsid
成功建立新會話的必要條件是呼叫程序不能是原來程序組的組長,而父程序有可能就是程序組的組長。新建立乙個子程序,這個子程序與父程序是乙個程序組的,所以子程序必不可能是程序組的組長,這就滿足了setsid
的前置條件。
呼叫setsid
建立乙個新會話,使得呼叫程序成為:1)新會話的首程序;2)新程序組的組長程序;3)沒有控制終端。
將當前工作目錄改為根目錄。因為子程序是繼承父程序的工作目錄,一般都是在乙個掛載的檔案系統中。因為守護程序通常在系統再引導前是一直存在的,所以如果守護程序的當前工作目錄在乙個掛載檔案系統中,那麼該檔案系統就不能被解除安裝。
關閉不再需要的檔案描述符。因為子程序會從父程序那裡繼承很多已經開啟的檔案描述符,但是基本都用不上,所以需要關閉。
開啟/dev/null
,使守護程序擁有檔案描述符0、1、2。在守護程序中如果寫出到標準輸出、標準錯誤或者從標準輸入中讀取時,因為相應的檔案描述符重定向到/dev/null
上,所以不會產生任何的效果。這本身是契合守護程序的定義的,因為守護程序不會與使用者終端進行互動,所以肯定也無法對標準輸出、標準輸入進行操作。
#include
#include
#include
#include
#include
#include
// 根據命令建立乙個守護程序
void
daemonize
(const
char
* cmd)
// 建立子程序,在子程序中執行命令if(
(pid =
fork()
)<0)
// 父程序,直接退出
else
if(pid >0)
// 建立乙個新的會話
// 子程序會成為新會話的受程序,並且沒有控制終端
// 相當於這個子程序獨佔這個會話,才不會收到會話內部控制終端的影響
setsid()
;// 確保未來不會獲取乙個控制終端
sa.sa_handler = sig_ign;
sigemptyset
(&sa.sa_mask)
; sa.sa_flags =0;
if(sigaction
(sighup,
&sa,
null
)<0)
// 解釋1:現在,子程序已經成為無終端的會話組長。但它可以重新申請開啟乙個控制終端。可以通過使程序不再成為會話組長來禁止程序重新開啟控制終端,也就是新建乙個孫子程序
// 解釋2:這裡需要fork一次的原因是:
// 通過setsid()已經讓程序所在的session沒有了controlling terminal,按理說是是符合daemon的要求了;
// 但是某些條件下,系統會給這些沒有controlling terminal的程序 「allocating the controlling terminal for a session」。
// 只要乙個process雖然所在的session是沒有controlling terminal的;
// 但是只要這個process是session leader,那麼還是有可能在某種情況下被觸發,讓系統給其分配controlling terminal,打破了daemon process的禁區。
// 那麼新建乙個孫子程序,並且讓子程序結束,這個孫子程序就不是session leader了(這個新的session的leader是子程序)if(
(pid =
fork()
)<0)
else
if(pid >0)
// 切換當前目錄為根目錄
// 防止如果檔案系統被解除安裝之後,守護程序會停止if(
chdir
("/"
)<0)
// 關閉繼承得到的所有檔案描述符
// 因為本身就不需要這些
if(rl.rlim_max == rlim_infinity)
rl.rlim_max =
1024
;for
(int i =
0; i < rl.rlim_max; i++
)// 將描述符0、描述符1、描述符2重定向到/dev/null
// 因為重定向到/dev/null,相當於不從標準輸入讀入也不寫出到標準輸出
// 這樣就可以確保守護程序不輸出到終端上,也不從終端上獲取輸入
fd0 =
open
("/dev/null"
, o_rdwr)
; fd1 =
dup(fd0)
; fd2 =
dup(fd0)
;// 初始化log file
// 因為所有訊息要輸出到log file中
openlog
(cdm, log_cons, log_daemon);if
(fd0 !=
0|| fd1 !=
1|| fd2 !=2)
}
APUE UNIX高階環境程式設計
1.重寫ls指令,用到了dirent.h標頭檔案,乙個目錄有關的標頭檔案,可以用man檢視 有2個結構體dir與struct dirent,目錄結構體與當前目錄下檔案 目錄結構體 如下 include include typedef struct dirent dirct intmain int a...
unix環境高階程式設計
unix 日曆時間 自1970 年1 月1 日00 00 00 以來的國際標準時間 utc 程序時間 cpu 時間 時鐘時間 程序執行時間的總量。使用者cpu 時間 執行使用者指令時間量。系統cpu 時間 執行核心所經歷時間。命令 time 第三章至第七章 原子操作 任何乙個要求多於1 個函式呼叫的...
unix環境高階程式設計
檔案描述符file descriptor通常是乙個小的非負整數,核心用以標識乙個特定程序正在訪問的檔案。當核心開啟乙個現有檔案或建立乙個新檔案時,它都返回乙個檔案描述符。在讀寫檔案時,可以私用這個檔案描述符。按管理,每當執行乙個新程式時,所有的shell都為其開啟了3個標準檔案描述符 標準輸入,標準...