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

查看字符串寫入的結果:
守護進程的執行結果
由上圖可以分析到,守護進程執行後,終端中並沒有打印信息。查找進程發現其在後臺繼續執行。查找/tmp/demo.log,可以看到每隔一段時間,就會向文件中寫入一串字符。

謝謝閱讀

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章