linux C守護進程編寫

 

 linux編程-守護進程編寫

    守護進程(Daemon)是運行在後臺的一種特殊進程。它獨立於控制終端而且週期性地執行某種任務或等待
    處理某些發生的事件。守護進程是一種頗有用的進程。 
    Linux的大多數服務器就是用守護進程實現的。好比,Internet服務器inetd,Web服務器httpd等。
    同時,守護進程完成許多系統任務。好比,做業規劃進程crond,打印進程lpd等。

  守護進程的編程自己並不複雜,複雜的是各類版本的Unix的實現機制不盡相同,
    形成不一樣 Unix環境下守護進程的編程規則並不一致。
    須要注意,照搬某些書上的規則(特別是BSD4.3和低版本的System V)到Linux會出現錯誤的。
    下面結合一些前輩的文檔和本身的例子說說守護進程的編程。


.基本概念
.進程
    .每一個進程都有一個父進程
    .當子進程終止時,父進程會獲得通知並能取得子進程的退出狀態。
.進程組
    .每一個進程也屬於一個進程組
    .每一個進程主都有一個進程組號,該號等於該進程組組長的PID號
    .一個進程只能爲它本身或子進程設置進程組ID號
.會話期
    .對話期(session)是一個或多個進程組的集合。
    .setsid()函數能夠創建一個對話期:
    若是,調用setsid的進程不是一個進程組的組長,此函數建立一個新的會話期。
    (1)此進程變成該對話期的首進程
    (2)此進程變成一個新進程組的組長進程。
    (3)此進程沒有控制終端,若是在調用setsid前,該進程有控制終端,那麼與該終端的聯繫被解除。
    若是該進程是一個進程組的組長,此函數返回錯誤。
    (4)爲了保證這一點,咱們先調用fork()而後exit(),此時只有子進程在運行,
    子進程繼承了父進程的進程組ID,可是進程PID倒是新分配的,因此不多是新會話的進程組的PID。
    從而保證了這一點。

    if((pid=fork())>0)  //parent
        exit(0);
    else if(pid==0){        //th1 child
        setsid();           //th1是成爲會話期組長
        if(fork() ==0){     //th2不會是會話期組長(變成孤兒進程組)
            ...
        }
    }


一. 守護進程及其特性
 
    (1)守護進程最重要的特性是後臺運行。在這一點上DOS下的常駐內存程序TSR與之類似。
    (2)其次,守護進程必須與其運行前的環境隔離開來。這些環境包括未關閉的文件描述符,控制終端,
    會話和進程組,工做目錄以及文件建立掩模等。這些環境一般是守護進程從執行它的父進程(特別是shell)
    中繼承下來的。
    (3)最後,守護進程的啓動方式有其特殊之處。它能夠在Linux系統啓動時從啓動腳本/etc/rc.d中啓動,
    能夠由做業規劃進程crond啓動,還能夠由用戶終端(一般是 shell)執行。
  總之,除開這些特殊性之外,守護進程與普通進程基本上沒有什麼區別。
    所以,編寫守護進程其實是把一個普通進程按照上述的守護進程的特性改形成爲守護進程。

二. 守護進程的編程要點  (來自UEAP)

  前面講過,不一樣Unix環境下守護進程的編程規則並不一致。所幸的是守護進程的編程原則其實都同樣,
      區別在於具體的實現細節不一樣。這個原則就是要知足守護進程的特性。
      同時,Linux是基於Syetem V的SVR4並遵循Posix標準,實現起來與BSD4相比更方便。編程要點以下;

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

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

2. 脫離控制終端,登陸會話和進程組
  進程屬於一個進程組,進程組號(GID)就是進程組長的進程號(PID)。登陸會話能夠包含多個進程組。
    這些進程組共享一個控制終端。這個控制終端一般是建立進程的登陸終端。
    控制終端,登陸會話和進程組一般是從父進程繼承下來的。
    咱們的目的就是要擺脫它們,使之不受它們的影響。
    方法是在第1點的基礎上,調用setsid()使進程成爲會話組長:

setsid();

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

3. 禁止進程從新打開控制終端
  如今,進程已經成爲無終端的會話組長。但它能夠從新申請打開一個控制終端。
    能夠經過使進程再也不成爲會話組長來禁止進程從新打開控制終端:

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

4. 關閉打開的文件描述符
  進程從建立它的父進程那裏繼承了打開的文件描述符。如不關閉,將會浪費系統資源,
    形成進程所在的文件系統沒法卸下以及引發沒法預料的錯誤。按以下方法關閉它們:

for(i=0;i 關閉打開的文件描述符close(i);>

5. 改變當前工做目錄
  進程活動時,其工做目錄所在的文件系統不能卸下。通常須要將工做目錄改變到根目錄。
    對於須要轉儲核心,寫運行日誌的進程將工做目錄改變到特定目錄如 /tmpchdir("/")

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

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

signal(SIGCHLD,SIG_IGN);

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

三. 守護進程實例
  守護進程實例包括兩部分:主程序test.c和初始化程序init.c。
    主程序每隔一分鐘向/tmp目錄中的日誌test.log報告運行狀態。
    初始化程序中的init_daemon函數負責生成守護進程。讀者能夠利用init_daemon函數生成本身的守護進程。 //------------------------------------------------------------------------
複製代碼
#include<unistd.h>
#include<signal.h> #include<stdio.h> #include<stdlib.h> #include<sys/param.h> #include<sys/types.h> #include<sys/stat.h> #include<time.h> void init_daemon() { int pid; int i; pid=fork(); if(pid<0) exit(1); //建立錯誤,退出 else if(pid>0) //父進程退出 exit(0); setsid(); //使子進程成爲組長 pid=fork(); if(pid>0) exit(0); //再次退出,使進程不是組長,這樣進程就不會打開控制終端 else if(pid<0) exit(1); //關閉進程打開的文件句柄 for(i=0;i<NOFILE;i++) close(i); chdir("/root/test"); //改變目錄 umask(0);//重設文件建立的掩碼 return; } void main() { FILE *fp; time_t t; init_daemon(); while(1) { sleep(60); //等待一分鐘再寫入 fp=fopen("testfork2.log","a"); if(fp>=0) { time(&t); fprintf(fp,"current time is:%s\n",asctime(localtime(&t))); //轉換爲本地時間輸出  fclose(fp); } } return; }
複製代碼

 

運行下面的命令:linux

cc testfork2.c -o testfork2shell

./testfork2編程

ps -ef|grep testfork2 能夠查找到對應的進程服務器

kill -9 1231殺死進程session

轉(參考):http://blog.csdn.net/zg_hover/article/details/2553321併發

相關文章
相關標籤/搜索