一個簡單的守護程序的編寫
阿新 • • 發佈:2019-01-28
建立子程序,父程序退出
這是編寫守護程序的第一步。由於守護程序是脫離控制終端的,因此,完成第一步後就會在Shell終端裡造成一程式已經執行完畢的假象。之後的所有工作都在子程序中完成,而使用者在Shell終端裡則可以執行其他命令,從而在形式上做到了與控制終端的脫離。 在Linux中父程序先於子程序退出會造成子程序成為孤兒程序,而每當系統發現一個孤兒程序時,就會自動由1號程序(init)收養它,這樣,原先的子程序就會變成init程序的子程序。在子程序中建立新會話
這個步驟是建立守護程序中最重要的一步,雖然它的實現非常簡單,但它的意義卻非常重大。在這裡使用的是系統函式setsid,在具體介紹setsid之前,首先要了解兩個概念:程序組和會話期改變當前目錄為根目錄
這一步也是必要的步驟。使用fork建立的子程序繼承了父程序的當前工作目錄。由於在程序執行中,當前目錄所在的檔案系統(如“/mnt/usb”)是不能解除安裝的,這對以後的使用會造成諸多的麻煩(比如系統由於某種原因要進入單使用者模式)。因此,通常的做法是讓"/"作為守護程序的當前工作目錄,這樣就可以避免上述的問題,當然,如有特殊需要,也可以把當前工作目錄換成其他的路徑,如/tmp。改變工作目錄的常見函式式chdir。
重設檔案許可權掩碼
檔案許可權掩碼是指遮蔽掉檔案許可權中的對應位。比如,有個檔案許可權掩碼是050,它就遮蔽了檔案組擁有者的可讀與可執行許可權。由於使用fork函式新建的子程序繼承了父程序的檔案許可權掩碼,這就給該子程序使用檔案帶來了諸多的麻煩。因此,把檔案許可權掩碼設定為0,可以大大增強該守護程序的靈活性。設定檔案許可權掩碼的函式是umask。在這裡,通常的使用方法為umask(0)。
關閉檔案描述符
同文件許可權碼一樣,用fork函式新建的子程序會從父程序那裡繼承一些已經打開了的檔案。這些被開啟的檔案可能永遠不會被守護程序讀寫,但它們一樣消耗系統資源,而且可能導致所在的檔案系統無法卸下。 在上面的第二步之後,守護程序已經與所屬的控制終端失去了聯絡。因此從終端輸入的字元不可能達到守護程序,守護程序中用常規方法(如printf)輸出的字元也不可能在終端上顯示出來。所以,檔案描述符為0、1和2 的3個檔案(常說的輸入、輸出和報錯)已經失去了存在的價值,也應被關閉。在不同的作業系統中問檔案描述符的最大值是不同的,這裡用linux中的65535(16位表示)。通常按如下方式關閉檔案描述符: =============================== for(i=0;i<MAXFILE;i++) close(i); ===============================守護程序退出處理
當用戶需要外部停止守護程序執行時,往往會使用 kill命令停止該守護程序。所以,守護程序中需要 編碼來實現kill發出的signal訊號處理,達到程序的正常退出。 =============================== signal(SIGTERM, sigterm_handler); void sigterm_handler(int arg) { _running = 0; } =============================== 這樣,一個簡單的守護程序就建立起來了。 示例程式碼:/*daemon.c*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
/*linux中檔案描述符的最大值是16位表示的65535*/
#define MAXFILE 65535
int main(void)
{
pid_t pid;
int i,fd,len;
char *buf = "This is daemon log!\n";
len = strlen(buf);
if( (pid=fork()) < 0 )
{
perror("Fork");
exit(1);
}
/*將父程序結束,實現終端脫離第一步*/
else if( pid > 0 )
exit(0);
/*這一步有三個功能
* 1.讓程序擺脫原會話的控制
* 2.讓程序擺脫原程序組的控制
* 3.讓程序擺脫原控制終端的控制*/
setsid();
/*改變當前目錄為根目錄*/
chdir("/");
/*重新設定檔案許可權掩碼*/
umask(0);
/*關閉檔案描述符*/
for(i=0;i<MAXFILE;i++)
{
close(i);
}
/*守護程序的服務部分
* 他的功能是每隔一秒向/tmp/daemon.log檔案中輸入字串*/
if( (fd=open("/tmp/daemon.log",O_CREAT | O_WRONLY | O_APPEND,0600)) < 0 )
{
perror("open");
exit(1);
}
while(1)
{
write(fd,buf,len);
sleep(1);
}
close(fd);
return 0;
}
這裡執行的時候,可以使用tail -f /tmp/daemon.log命令來動態追蹤檔案的內容,結束程序可以用ps -e | grep daemon來找到程序號,然後kill掉。