一.守護進程(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中,所有程序共同調用,但同時只有一個程序執行守護代碼。使用內存文件映射保存信息。這樣沒有單獨的守護進程,只要有一個進程在運行,就能保證守護代碼在執行。 再或者可以考慮使用看門狗的方式。進程給一塊空間定時更新數據,守護代碼定時檢 測數據更新時間,如果某一進程數據到一定時間超時未更新認爲此進程掛掉,重啓這個進程。