(一)、守護進程的特徵
#ps -axj 其中:-a 顯示所有用戶所擁有的進程的狀態
-x 顯示沒有控制終端的進程狀態
-j 顯示與作業相關的的信息:會話ID、進程組ID、控制終端以及終端進程組ID
(二)守護進程的編程規則
(1)、編程規則
1.調用umask()將文件模式創建屏蔽字設置爲0.(文件權限——不要變更程序的初始文件權限mode)
由繼承提來的文件模式創建屏蔽字可能會拒絕設置某些權限。例如,若守護進程要創建一個組可讀、寫的文件,而繼承的文件模式創建屏蔽字可能屏蔽了這兩種權限,於是所要求的組可讀、寫就不能起作用。
例:umask(0);mode與上umask反碼
2.調用fork(),產生一個子進程,然後使父進程退出(exit),子進程變爲孤兒進程(init進程的子進程)
這樣可以實現:
1)、如果該守護進程是作爲一條簡單shell命令啓動的,那麼父進程終止使得shell認爲這條命令已經執行完畢(騙了一下os)
2)、子進程繼承了父進程組ID,但具有一個新的進程ID,這就保證了子進程不是一個進程組的組長進程
if ((pid = fork()) < 0) perror("%s: can't fork");
else if (pid != 0) exit(0); /* parent */
3. 調用setsid()以創建一個新會話(因爲父進程退出了,父進程的會話也退出了)
這樣可以實現: 1)、成爲新會話的首進程
2)、成爲一個新進程組的組長進程
3)、沒有終端控制
例:setsid();
4.將當前的工作目錄更改爲根目錄
從父進程處繼承過來的當前工作目錄可能在一個裝配文件系統中。因爲守護進程通常在系統再引導之前是一直存在的,所以如果守護進程的當前工作目錄在一個裝配文件系統中,那麼該文件系統就不能被umount,這與裝配文件系統的原意不符
if (chdir("/")< 0) perror("can't changedirectory to /");
5、關閉不再需要的文件描述符(守護進程無輸入輸出,文件描述符繼承自父進程,守護進程不需要,全部關閉)
這使守護進程不再持有從其父進程繼承過來的某些文件描述符(父進程可能是shell進程,或某個其它進程),可以使用getrlimit()函數來判定最高文件描述符值,並關閉直到該值的所有描述符
Shell指令:#ulimit -a
if (rl.rlim_max == RLIM_INFINITY) rl.rlim_max = 1024;//os只允許一個進程打開1024個文件
for (i = 0; i < rl.rlim_max; i++) close(i);//系統調用
補充:資源限制
系統資源的限制可由getrlimit讀取,由setrlimit設置
#include <sys/resource.h>
int getrlimit(int resource, struct rlimit*r_limit);
int setrlimit(int resource, const structrlimit *r_limit);
其中:struct rlimit定義於sys/resource.h,至少包括下列兩個成員
{ rlim_t rlim_cur; 當前的軟限制(當前文件打開數>1024時,發出警告)
rlim_t rlim_max; 硬限制(當前文件打開數>1024時,由OS殺死進程)
}
rlim_t是一個整數類型用於描述資源級別(typdefunsigned int rlim_t)
通常soft limit是一建議限制,意味着不應超越,如果超越會引起庫函數返回錯誤
Hard limit是硬性限制,若超越意味着將導致系統發送信號以終結程序的運行
例:若超越CPU時間,將導致內核發送SIGXCPU
若超越數據大小限制,將導致內核發送SIGSEGV
一個程序可以設置軟限制,一般軟限制小於hard limit
僅有具有superuser privileges的程序可以增大hard limit
可以被限制的資源被定義於sys/resource.h中,包括:
CPU使用量
數據段大小
文件大小(<4G)
打開文件數(一般1024)
每個進程用的堆棧大小
每個進程用的地 址空間,一般小於4G
返回值:Successful 0
error -1
6、某些守護進程打開/dev/null(空洞設備)使其具有文件描述符0(stdin)、1(stdout)和2(stderr)
這樣任何一個試圖讀標準輸入、寫標準輸出或標準出錯的庫例程都不會產生任何效果
因爲守護進程並不與終端設備相關聯,所以不能在終端設備上顯示其輸出,也無處從交互用戶那裏接收輸入。
fd0= open("/dev/null", O_RDWR);// 0(stdin)、1(stdout)和2(stderr)全部放進/dev/null(空洞設備)中
fd1 = dup(0);
fd2 = dup(0);
(2)、syslog設施(Linux/UNIX:消息寫入syslog設施——寫入log文件中)
守護進程存在的一個問題是如何處理出錯消息。因爲它本就不應該有控制終端,所以不能只是簡單地寫到標準錯誤上。也不希望所有守護進程都寫到控制檯(console)設備上,因爲在很多工作站上控制檯設備都運行着一個窗口系統。也不希望每個守護進程將它自己的出錯消息寫到一個單獨的文件中。所以,需要有一個集中的守護進程出錯記錄設施。BSD的syslog設施得到了廣泛的應用。大多數守護進程都使用這一設施。
有3種產生日誌消息的方法:
1)內核例程可以調用log函數。
任何一個用戶進程都可以通過打開(open)並讀取(read) /dev/klog設備來讀取這些消息。
2)大多數用戶進程(守護進程)調用syslog(3)函數來產生日誌消息,這使消息被髮送至UNIX域數據報套接字/dev/log
3)無論一個用戶進程是在此主機上,還是在通過TCP/IP網絡連接到此主機的其它主機上,都可以將日誌消息發向UDP 514端口。注意syslog函數從不產生這些UDP數據報,它們要求產生此日誌消息的進程進行顯式的網絡編程。
通常,syslogd守護進程讀取所有3種格式的日誌消息。此守護進程在啓動時讀一個配置文件---/etc/syslog.conf,該文件決定了不同種類的消息應送向何處
2、syslog設施的接口syslog函數
#include <syslog.h>
void openlog(const char *ident, int option, int facility ); 可選
voidsyslog(int priority, const char *format, ...); 必須
void closelog(void); 可選
說明:調用openlog是可選的。如果不調用openlog,則在第一次調用syslog時,自動調用openlog,
調用closelog也是可選的,因爲它只是關閉曾被用於與syslogd守護進程進行通信的描述符
void syslog(int priority, const char *format , ...); 必須
作用:產生一個日誌消息,默認打開/dev/log設備
參數:1)priority參數(級別)是facility和level的組合,它們可選取的值分別列於facility和level中 level值按優先級從最高到最低依次排列
2)format參數(自定義的格式):
將format參數以及其它所有參數傳到vsprintf函數以便進行格式化。在format中,每個出現的%m字符都先被代換成與errno值對應的出錯消息字符串(strerror)
(四)、實現函數---daemon()
作用:使調用程序run in the background,進程成爲守護進程
函數:#include <unistd.h>//系統調用
intdaemon(int nochdir, int noclose);
參數:
nochdir 若爲0,daemon()將調用進程的當前目錄更改爲“/ ”
否則,當前進程的工作目錄無變化
noclose 若爲0,(關)daemon()redirects standard input, standard output and standard error to /dev/null;
否則 no changes are made to these file descriptors.
返回值 //成功與否的標誌
On success daemon() returns 0.
If an error occurs, daemon() returns -1 andsets errno to any of the errors specified for the fork and setsid