LinuxC/C++編程(7)—守護進程

        守護進程(Daemon)是運行在後臺的一種特殊進程。它獨立於控制終端並且週期性地執行某種任 務或等待處理某些發生的事件。

        守護進程是一種很有用的進程。Linux的大多數服務器就是用守護進程實現的。比如,Internet服務器 inetd,Web服務器httpd等。同時,守護進程完成許多系統任務。比如,作業規劃進程crond,打印進程lpd等。

        假如不依賴Linux提供的daemon()函數,那麼守護進程的常見編程方法如下:


1. 屏蔽一些有關控制終端操作的信號。
這是爲了防止在守護進程沒有正常運轉起來時,控制終端受到干擾退出或掛起。示例如下:

signal(SIGTTOU,SIG_IGN); 
signal(SIGTTIN,SIG_IGN); 
signal(SIGTSTP,SIG_IGN); 
signal(SIGHUP ,SIG_IGN);
		

 所有的信號都有自己的名字。這些名字都以“SIG”開頭,只是後面有所不同。開發人員可以通過這些名字瞭解到系統中發生了什麼事。當信號出現時,開發人員可以要求系統進行以下三種操作:

 忽略信號。大多數信號都是採取這種方式進行處理的,這裏就採用了這種用法。但值得注意的是對SIGKILL和SIGSTOP信號不能做忽略處理。
 捕捉信號。最常見的情況就是,如果捕捉到SIGCHID信號,則表示子進程已經終止。然後可在此信號的捕捉函數中調用waitpid()函數取得該子進程的進程ID和它的終止狀態。另外,如果進程創建了臨時文件,那麼就要爲進程終止信號SIGTERM編寫一個信號捕捉函數來清除這些臨時文件。
 執行系統的默認動作。對絕大多數信號而言,系統的默認動作都是終止該進程。對這些有關終端的信號,一般採用忽略處理,從而保障了終端免受干擾。
 這類信號分別是,SIGTTOU(表示後臺進程寫控制終端)、SIGTTIN(表示後臺進程讀控制終端)、SIGTSTP(表示終端掛起)和SIGHUP(進程組長退出時向所有會議成員發出的)。

2. 在後臺運行。
爲避免掛起控制終端將Daemon放入後臺執行。方法是在進程中調用fork使父進程終止,讓Daemon在子進程中後臺執行。

if(pid=fork())
exit(0);//是父進程,結束父進程,子進程繼續

3. 脫離控制終端,登錄會話和進程組
有必要先介紹一下Linux中的進程與控制終端,登錄會話和進程組之間的關係:進程屬於一個進程組,進程組號(GID)就是進程組長的進程號(PID)。登錄會話可以包含多個進程組。這些進程組共享一個控制終端。這個控制終端通常是創建進程的登錄終端。
控制終端,登錄會話和進程組通常是從父進程繼承下來的。我們的目的就是要擺脫它們,使之不受它們的影響。方法是在第1點的基礎上,調用setsid()使進程成爲會話組長:

setsid();

說明:當進程是會話組長時setsid()調用失敗。但第一點已經保證進程不是會話組長。setsid()調用成功後,進程成爲新的會話組長和新的進程組長,並與原來的登錄會話和進程組脫離。由於會話過程對控制終端的獨佔性,進程同時與控制終端脫離。

4. 禁止進程重新打開控制終端
現在,進程已經成爲無終端的會話組長。但它可以重新申請打開一個控制終端。可以通過使進程不再成爲會話組長來禁止進程重新打開控制終端:

if(pid=fork())
exit(0);//結束第一子進程,第二子進程繼續(第二子進程不再是會話組長)

5. 關閉打開的文件描述符
進程從創建它的父進程那裏繼承了打開的文件描述符。如不關閉,將會浪費系統資源,造成進程所在的文件系統無法卸下以及引起無法預料的錯誤。按如下方法關閉它們(NOFILE在頭文件中定義):

for(i=0;i < NOFILE;i++)
close(i);

6. 改變當前工作目錄
進程活動時,其工作目錄所在的文件系統不能卸下。一般需要將工作目錄改變到根目錄。對於需要轉儲核心,寫運行日誌的進程將工作目錄改變到特定目錄如/tmp:

chdir("/tmp")

7. 重設文件創建掩模
進程從創建它的父進程那裏繼承了文件創建掩模。它可能修改守護進程所創建的文件的存取位。爲防止這一點,將文件創建掩模清除:

umask(0);

8. 處理SIGCHLD信號
處理SIGCHLD信號並不是必須的。但對於某些進程,特別是服務器進程往往在請求到來時生成子進程處理請求。如果父進程不等待子進程結束,子進程將成爲殭屍進程(zombie)從而佔用系統資源。如果父進程等待子進程結束,將增加父進程的負擔,影響服務器進程的併發性能。在Linux下可以簡單地將 SIGCHLD信號的操作設爲SIG_IGN。

signal(SIGCHLD,SIG_IGN);

這樣,內核在子進程結束時不會產生殭屍進程。這一點與BSD4不同,BSD4下必須顯式等待子進程結束才能釋放殭屍進程。


但是,以上都是廢話,因爲Linux中有直接創建守護進程的函數:


函數介紹:

#include <unistd.h>

int daemon(int nochdir, int noclose);

1. daemon()函數主要用於希望脫離控制檯,以守護進程形式在後臺運行的程序。如果調用成功,父進程將直接通過exit(2)退出,失敗則返回-1。

2. 當nochdir爲0時,daemon將更改進城的根目錄爲root(“/”)。

3. 當noclose爲0是,daemon將進城的STDIN, STDOUT, STDERR都重定向到/dev/null。


寫個相關的例子試一下就知道了,創建一個守護進程,並讓他定期向一個文件寫字符串。

直接貼代碼:

//============================================================================
// Name        : DaemonTest.cpp
// Author      : Lin
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <iostream>
#include <unistd.h>
#include <cstdio>
#include <string.h>
using namespace std;


int main() {
	if (daemon(1,0) < 0) //包含於<unistd.h>,改變目錄,但關閉終端的控制
	{
		cerr << "daemon error!" << endl;
		return -1;
	}
	char *path = "/home/lin/桌面/test";
	FILE* file = fopen(path, "w+");
	char *text = "heiheihei\n";
	for (int i = 0; i != 10; ++i)
	{
		fwrite(text, 1, strlen(text), file);
		sleep(2);
	}
	fclose(file);
	return 0;
}


      生成可執行文件後運行一下:

# ./DaemonTest
      通過常見的兩個指令可以來查看這個後臺進程的狀態:

# ps -e | grep DaemonTest
# lsof | grep DaemonTest
     大概就這些東西了~



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