linux基礎之守護進程

一.守護進程(Daemon)

1.關於守護進程

守護進程,顧名思義,也就是專門守護一個進程的進程。守護進程的職責就是專門確保被指定的進程的運行。
守護進程也稱精靈進程(Daemon),是運行在後臺的一種特殊進程。它獨立於控制終端,並且週期性的執行某種任務或等待處理某些發生的事件。守護進程是一種很有用的進程,linux的大多數服務器就是用守護進程實現的。比如:Internet服務器inetd。Wed服務器httpd等。同時,守護進程完成了許多系統任務。比如,作業規劃進程crond等。

linux系統啓動時會啓動很多系統服務進程,這些服務進程沒有控制終端,不能和用戶交互。其他進程都是在用戶登錄或運行程序時創建,在運行結束或用戶註銷時終止,但是系統服務進程不受用戶登錄註銷的影響,他們一直在運行着。這種進程的名稱叫做守護進程。


最直接的應用就是重啓進程。如果我們的軟件崩潰了,爲了讓軟件重新運行起來,軟件本身是無法不方便或者無法做到的。因爲出故障就是軟件本身,而這個故障嚴重到迫使軟件本身掛掉了,自然也沒有辦法重啓自己了。
那麼可以藉助一個專門的進程來幫助自己啓動咯。守護進程當然不僅限於重啓進程咯。要實現什麼守護功能,就看你怎麼實現了。你可以守護進程不被關閉,重啓進程,也可以守護進程的網絡,必要的配置文件等等。
守護進程好比是保鏢,被守護的進程就是保鏢保護的對象。守護進程在很多地方都會有應用

二.創建守護進程

1. 關於setsid函數

創建守護進程最關鍵一步是調用setsid函數創建一個新的Session,併成爲Session Leader。

#include<unistd.h>
pid_t setsid(void);
  • 該函數調用成功時返回新創建的Session的id(其實也就是當前進程的id),出錯返回-1。注意:調用這個函數之前,當前進程不允許是進程組的Leader,否則該函數返回-1。要保證當前進程不是進程組的Leader很容易,只要先fork再調用setsid就行了。fork的子進程和父進程在同一個進程組中,進程組的Leader必然是該進程組的第一個進程,所以子進程不可能是該組的第一個進程,所以在子進程中調用setsid是一定不會有問題的。

  • 成功創建該函數的結果是:

    • 創建一個新的Seccion,當前進程成爲Session Leader,當前進程的id就是Session的id。
    • 創建一個新的進程組,當前進程成爲進程組的Leader,當前進程的id就是進程組的id。
    • 如果當前進程原本有一個控制終端,則他失去這個控制終端,成爲一個沒有控制終端的進程。所謂失去控制終端是指,原來的控制終端仍然是打開的,仍然可以讀寫,但是隻是一個普通的打開文件,而不是控制終端。
  • 查看守護進程

ps axj |grep -E 'd$'

這裏寫圖片描述
TPGID一欄寫着-1的都是沒有控制終端的進程,也就是守護進程.
由上圖可以看出1.我們上篇博客中提到的crond也爲守護進程。2. 守護進程都是孤兒進程,因爲其父進程的id爲0;守護進程自成會話,自稱進程組。

2.創建一個守護進程的流程

  • 調用umask將文件模式創建屏蔽字設置爲0;
  • 調用fork,父進程退出(exit).原因:1.如果該守護進程是作爲一個簡單的shell命令啓動的,那麼父進程終止使得shell認爲該命令已經執行完畢。2.保證子進程不是一個進程組的組長進程。
  • 調用setsid創建一個新的會話。 setsid會導致:1 調用進程成爲新會話的首進程。2 調用的進程成爲進程組的組長進程。3 調用進程沒有控制終端。(在此fork一次,保證daemon進程,之後不會打開tty設備)
  • 將當前工作目錄更改爲根目錄。
  • 關閉不再需要的文件描述符。
  • 其他:忽略SIGCHLD信號。

3.創建代碼

#include<stdio.h>
#include<unistd.h>
#include<signal.h>

void creat_daemon()
{
    umask(0);
    pid_t id = fork();
    if(id > 0)//father
    {
        return ;
    }
    else if(id < 0)
    {
        perror("fork");
        return ;
    }

    setsid();

    close(0);
    close(1);
    close(2);

    signal(SIGCHLD,SIG_IGN);

    chdir("/");
}
int main()
{
    creat_daemon();
    while(1)
    {
        sleep(1);
    }
    return 0;
}

運行結果
這裏寫圖片描述

4.其他

在網上我們可以看到另外一種版本是用兩次fork來創建守護代碼如下:

#include<stdio.h>
#include<unistd.h>
#include<signal.h>

void creat_daemon()
{

    umask(0);
    pid_t id = fork();
    if(id > 0)//father
    {
        return ;
    }
    else if(id < 0)
    {
        perror("fork");
        return ;
    }

    setsid();

    signal(SIGCHLD,SIG_IGN);

    if(id = fork()<0)
    {
        printf("fork errot\n");
        return ;
    }
    else if(id!=0)
    {
        return;
    }

    if(chdir("/")<0)
    {
        printf("chdir error\n");
        return ;
    }
    close(0);
    close(1);
    close(2);
}
int main()
{
    creat_daemon();
    while(1)
    {
        sleep(1);
    }
    return 0;
}

結果如下:
這裏寫圖片描述
原因如下:

其實現就是心跳互相檢測對方是否存在,不存在就啓動對方。心跳可使用socket或共享內存什麼的。
如果進程比較多,可單獨啓動一個守護進程進行檢測所有進程。守護進程比較簡單,出錯的可能性比較小。
再或者守護部分寫到dll中,所有程序共同調用,但同時只有一個程序執行守護代碼。使用內存文件映射保存信息。這樣沒有單獨的守護進程,只要有一個進程在運行,就能保證守護代碼在執行。 再或者可以考慮使用看門狗的方式。進程給一塊空間定時更新數據,守護代碼定時檢 測數據更新時間,如果某一進程數據到一定時間超時未更新認爲此進程掛掉,重啓這個進程。

發佈了63 篇原創文章 · 獲贊 29 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章