linux C 守護進程及其創建

涉及的概念:
守護進程:在linux或者unix操作系統中在系統引導的時候會開啓很多服務,這些服務就叫做守護進程。爲了增加靈活性,root可以選擇系統開啓的模式,這些模式叫做運行級別,每一種運行級別以一定的方式配置系統。守護進程是脫離於終端並且在後臺運行的進程。守護進程脫離於終端是爲了避免進程在執行過程中的信息在任何終端上顯示並且進程也不會被任何終端所產生的終端信息所打斷。守護進程,也就是通常說的Daemon進程,是Linux中的後臺服務進程。它是一個生存期較長的進程,通常獨立於控制終端並且週期性地執行某種任務或等待處理某些發生的事件。守護進程常常在系統引導裝入時啓動,在系統關閉時終止。Linux系統有很多守護進程,大多數服務都是通過守護進程實現的,同時,守護進程還能完成許多系統任務,例如,作業規劃進程crond、打印進程lqd等(這裏的結尾字母d就是Daemon的意思)。由於在Linux中,每一個系統與用戶進行交流的界面稱爲終端,每一個從此終端開始運行的進程都會依附於這個終端,這個終端就稱爲這些進程的控制終端,當控制終端被關閉時,相應的進程都會自動關閉。但是守護進程卻能夠突破這種限制,它從被執行開始運轉,直到整個系統關閉時才退出。如果想讓某個進程不因爲用戶或終端或其他地變化而受到影響,那麼就必須把這個進程變成一個守護進程。
進程組:是一個或多個進程的集合。進程組有進程組ID來唯一標識。除了進程號(PID)之外,進程組ID也是一個進程的必備屬性。每個進程組都有一個組長進程,其組長進程的進程號等於進程組ID。且該進程組ID不會因組長進程的退出而受到影響。
會話週期:會話期是一個或多個進程組的集合。通常,一個會話開始與用戶登錄,終止於用戶退出,在此期間該用戶運行的所有進程都屬於這個會話期。

創建守護進程的步驟:
1、讓程序在後臺運行,創建子進程,父進程退出

   由於守護進程是脫離控制終端的,因此,完成第一步後就會在Shell終端裏造成一程序已經運行完畢的假象。之後的所有工作都在子進程中完成,而用戶在Shell終端裏則可以執行其他命令,從而在形式上做到了與控制終端的脫離。在Linux中父進程先於子進程退出會造成子進程成爲孤兒進程,而每當系統發現一個孤兒進程時,就會自動由init進程收養,這樣,原先的子進程就會變成init進程的子進程。
2、在子進程中創建新會話,即調用setsid創建一個新的對話期
      首先具體介紹setsid的相關內容:
      (1)setsid函數作用:

        setsid函數用於創建一個新的會話,並擔任該會話組的組長。調用setsid有下面的3個作用:

       讓進程擺脫原會話的控制

       讓進程擺脫原進程組的控制

  讓進程擺脫原控制終端的控制

那麼,在創建守護進程時爲什麼要調用setsid函數呢?由於創建守護進程的第一步調用了fork函數來創建子進程,再將父進程退出。由於在調用了fork函數時,子進程全盤拷貝了父進程的會話期、進程組、控制終端等,雖然父進程退出了,但會話期、進程組、控制終端等並沒有改變,因此,還還不是真正意義上的獨立開來,而setsid函數能夠使進程完全獨立出來,函數調用成功後,進程成爲新的對話組長和進程組長,並與原來的登陸對話和進程組脫離,由於對話進程對控制終端的獨佔性,進程與控制終端脫離。

3、禁止進程重新打開控制終端。

    以上步驟後,進程已經成爲一個無終端的會話組長,但是它可以重新申請打開一個終端。爲了避免這種情況的發生,可以通過使進程不再是會話組長來實現。再通過調用fork函數創建子進程,使調用fork函數的進程退出。

4、關閉不再需要的文件描述符。

    創建的子進程從父進程繼承無法打開的文件描述符。這些被打開的文件可能永遠不會被守護進程讀寫,但它們一樣消耗系統資源。如不關閉將會浪費系統資源,造成系統所在的文件系統無法卸下以及無法預料的錯誤。先得到最高的文件文件描述符值,然後循環關閉0到最高文件描述值的所有文件描述符。

如下:

for(i=0;i<MAXFILE;close(i++))
{
      ;
}

5、改變當前目錄爲根目錄

    使用fork創建的子進程繼承了父進程的當前工作目錄。由於在進程運行中,當守護進程當前工作目錄是在一個裝配文件系統中時,(如“/mnt/usb”)是不能卸載的,這對以後的使用會造成諸多的麻煩(比如系統由於某種原因要進入用戶模式)。因此,通常的做法是讓"/"作爲守護進程的當前工作目錄,這樣就可以避免上述的問題,當然,如有特殊需要,也可以把當前工作目錄換成其他的路徑,如/tmp。改變工作目錄的常見函數式chdir。
6、重設文件權限掩碼,將文件創建時使用的屏蔽字設置爲0。
   文件權限掩碼是指屏蔽掉文件權限中的對應位。比如,有個文件權限掩碼是050,它就屏蔽了文件組擁有者的可讀與可執行權限。由於使用fork函數新建的子進程繼承了父進程的文件權限掩碼,可能會拒絕某些許可權。因此,把文件權限掩碼設置爲0,可以大大增強該守護進程的靈活性。設置文件權限掩碼的函數是umask。在這裏,通常的使用方法爲umask(0)。
7、處理SIGCHLD信號。
    這一步並不是必須的,但對於某些進程,特別是服務器進程往往是在請求到來時生成子進程處理請求。如父進程不等待子進程結束,子進程將成爲殭屍進程,從而佔用系統資源。如果父進程等待子進程結束,將增加父進程的負擔,影響服務器性能的併發性能。在Linux下可以簡單的將SIGCHLD信號的操作設爲SIG_IGN。這樣,子進程結束時不會產生殭屍進程。
  

這樣,一個簡單的守護進程就建立起來了。                                     

   ps:參考《linux C 編程實戰》
實例:

=====================================================================

  /*
 * =====================================================================================
 *
 *       Filename:  daemon2.c
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  2013年07月24日 10時21分31秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  gaoyuan, 
 *        Company:  Class 1204 of Computer Science and Technology
 *
 * =====================================================================================
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <syslog.h>

#define MAXFILE 65535

int main(int argc, char * argv[])
{
	pid_t pc;
	int i, fd, len;

	/*忽略終端I/O信號,STOP信號*/
	signal(SIGTTOU, SIG_IGN);
	signal(SIGTTIN, SIG_IGN);	
	signal(SIGTSTP, SIG_IGN);
	signal(SIGHUP, SIG_IGN);
	
	pc = fork(); 
	if (pc < 0)
	{
		printf("error fork\n");
		exit(1);
	}
	else if(pc > 0)
	{
		exit (0);    //結束父進程,使子子進程成爲後臺進程
	}

	/*新建一個進程組,在新的進程組中,子進程成爲這個進程組的首進程,使該進程脫離所有終端*/
	setsid();

	/*再次新建一個進程, 退出父進程, 保證該進程不是進程組長, 同時讓該進程無法打開一個終端*/
	pc = fork(); 
	if (pc < 0)
	{
		printf("error fork\n");
		exit(1);
	}
	else if(pc > 0)
	{
		exit (0);    
	}
	
	/*關閉所有文件描述符*/
	for(i = 0;i < MAXFILE;close(i++)) 
	{
		;
	}
	
	chdir("/"); //改變工作目錄,使進程不與任何文件系統聯繫*/
		
	umask(0); //文件屏蔽字設置爲0

	signal(SIGCHLD, SIG_IGN);   //忽略SIGCHLD信號

	/*每隔10s寫一句話*/
	while(1)
	{
		sleep(10);
		syslog(LOG_USER|LOG_INFO, "測試守護進程\n");
	}
}


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