天天看點

Linux下守護程序的原理及代碼實作

守護程序是脫離于終端并且在背景運作的程序。

守護程序通常獨立于控制終端并且周期性地執行某種任務或等待處理某些發生的事情。守護程序常常在系統引導裝入時啟動,在系統關閉時終止。Linux系統有很多守護程序,大多數服務都是通過守護程序實作的,同時,守護程序還能完成許多系統任務。

建立一個簡單的守護程序的步驟如下:

(1) 建立子程序,父程序退出

這一步完成後,在shell終端裡造成一程式已經運作完畢的假象。之後的所有工作都在子程序中完成,而使用者在shell終端裡則可以執行其他指令,進而在形式上做到了與控制終端的脫離。在Linux中,如果父程序先于子程序退出會造成子程序稱為孤兒程序,而每當系統發現一個孤兒程序時,就會自動由1号程序(init)收養它,這樣,原先的子程序就會變成init程序的子程序。

(2) 使用setsid()系統函數,在子程序中建立新會話

setsid()用于建立一個新的會話,并擔任該會話組的組長。

其具體作用是:

1)讓程序擺脫原會話的控制

2)讓程序擺脫原程序組的控制

3)讓程序擺脫原控制終端的控制

由于建立守護程序的第一步是調用fork函數,子程序全盤拷貝了父程序的會話期、程序組、控制終端等,是以還不是真正意義上的獨立。而setsid函數能夠使程序完全獨立出來,進而擺脫其他程序的控制。

(3) 改變目前目錄為根目錄

子程序繼承了父程序的工作目錄,在程序運作中,目前目錄所在的檔案系統是不能解除安裝的,這會對以後的使用造成麻煩。是以,通常的做法是讓根目錄作為守護程序的目前工作目錄。

(4) 重新設定檔案權限掩碼

子程序繼承了父程序的檔案權限掩碼(指屏蔽掉檔案權限中的對應位),這可能導緻子程序無法讀或寫檔案,是以設定檔案權限掩碼為0,可增強該守護程序的靈活性。

(5)關閉檔案描述符

子程序會從父程序中繼承的一些已經打開的檔案,而這些被打開的檔案可能永遠不會被守護程序讀寫,但會消耗系統資源,而且可能導緻所在的檔案系統無法結束。是以關閉掉子程序中的檔案描述符也是有必要的。

具體代碼實作如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/stat.h>

#define MAXFILE 65535

int main()
{
	// 建立子程序
	pid_t pc;	
	pc = fork();
	if(pc < 0)
	{
		printf("fork error \n");
		exit(1);
	}
	else if(pc > 0)
	{
		// 父程序退出
		exit(0);
	}
	
	// 子程序與父程序分離
	setsid(); 
	// 設定目前目錄為根目錄
	chdir("/");
	// 重新設定檔案權限掩碼
	umask(0); 
	// 關閉檔案描述符
	for(int i = 0; i < MAXFILE; ++i)
	    close(i);
	    
	char *buf = "this is a demo\n";
	int len = strlen(buf);
	int fd;	
	
	// 每隔5s向/tmp/demo.log中寫入一串字元
    while(1)
    {
		if((fd = open("/tmp/demo.log",O_CREAT|O_WRONLY|O_APPEND,0600)) < 0)
		{
			perror("open");
			exit(1);
		}
		write(fd,buf,len+1);
		close(fd);
		sleep(10);
  }
  return 0;
}
           

上述代碼在Linux下使用gcc或g++進行編譯:

g++ guard.cpp -o guard

檢視字元串寫入的結果:

Linux下守護程式的原理及代碼實作

由上圖可以分析到,守護程序執行後,終端中并沒有列印資訊。查找程序發現其在背景繼續執行。查找/tmp/demo.log,可以看到每隔一段時間,就會向檔案中寫入一串字元。

謝謝閱讀

參考書籍 《背景開發 核心技術與應用實踐》

繼續閱讀