以下講的是守護進程的原理,linux下不用這么麻煩,但是看看還是有必要的。轉自:http://www.cppblog.com/tx7do/articles/5963.html
Linux守護進程的編程方法
守護進程(Daemon)是運行在后台的一種特殊進程。它獨立於控制終端並且周期性地執行某種任 務或等待處理某些發生的事件。守護進程是一種很有用的進程。Linux的大多數服務器就是用守護進程實現的。比如,Internet服務器 inetd,Web服務器httpd等。同時,守護進程完成許多系統任務。比如,作業規划進程crond,打印進程lpd等。
守護進程的編程本 身並不復雜,復雜的是各種版本的Unix的實現機制不盡相同,造成不同Unix環境下守護進程的編程規則並不一致。這需要讀者注意,照搬某些書上的規則 (特別是BSD4.3和低版本的System V)到Linux會出現錯誤的。下面將全面介挺不錯紹Linux下守護進程的編程要點並給出詳細實例。
一. 守護進程及其特性
守 護進程最重要的特性是后台運行。在這一點上DOS下的常駐內存程序TSR與之相似。其次,守護進程必須與其運行前的環境隔離開來。這些環境包括未關閉的文 件描述符,控制終端,會話和進程組,工作目錄以及文件創建掩模等。這些環境通常是守護進程從執行它的父進程(特別是shell)中繼承下來的。最后,守護 進程的啟動方式有其特殊之處。它可以在Linux系統啟動時從啟動腳本/etc/rc.d中啟動,可以由作業規划進程crond啟動,還可以由用戶終端 (通常是shell)執行。
總之,除開這些特殊性以外,守護進程與普通進程基本上沒有什么區別。因此,編寫守護進程實際上是把一個普通進程按照上述的守護進程的特性改造成為守護進程。如果讀者對進程有比較深入的認識就更容易理解和編程了。
二. 守護進程的編程要點
前 面講過,不同Unix環境下守護進程的編程規則並不一致。所幸的是守護進程的編程原則其實都一樣,區別在於具體的實現細節不同。這個原則就是要滿足守護進 程的特性。同時,Linux是基於Syetem V的SVR4並遵循Posix標准,實現起來與BSD4相比更方便。編程要點如下;
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在子進程中后台執行。
exit(0);//是父進程,結束父進程,子進程繼續
3. 脫離控制終端,登錄會話和進程組
有必要先介紹一下Linux中的進程與控制終端,登錄會話和進程組之間的關系:進程屬於一個進程組,進程組號(GID)就是進程組長的進程號(PID)。登錄會話可以包含多個進程組。這些進程組共享一個控制終端。這個控制終端通常是創建進程的登錄終端。
控制終端,登錄會話和進程組通常是從父進程繼承下來的。我們的目的就是要擺脫它們,使之不受它們的影響。方法是在第1點的基礎上,調用setsid()使進程成為會話組長:
說明:當進程是會話組長時setsid()調用失敗。但第一點已經保證進程不是會話組長。setsid()調用成功后,進程成為新的會話組長和新的進程組長,並與原來的登錄會話和進程組脫離。由於會話過程對控制終端的獨占性,進程同時與控制終端脫離。
4. 禁止進程重新打開控制終端
現在,進程已經成為無終端的會話組長。但它可以重新申請打開一個控制終端。可以通過使進程不再成為會話組長來禁止進程重新打開控制終端:
exit(0);//結束第一子進程,第二子進程繼續(第二子進程不再是會話組長)
5. 關閉打開的文件描述符
進程從創建它的父進程那里繼承了打開的文件描述符。如不關閉,將會浪費系統資源,造成進程所在的文件系統無法卸下以及引起無法預料的錯誤。按如下方法關閉它們(NOFILE在頭文件中定義):
close(i);
6. 改變當前工作目錄
進程活動時,其工作目錄所在的文件系統不能卸下。一般需要將工作目錄改變到根目錄。對於需要轉儲核心,寫運行日志的進程將工作目錄改變到特定目錄如/tmp:
7. 重設文件創建掩模
進程從創建它的父進程那里繼承了文件創建掩模。它可能修改守護進程所創建的文件的存取位。為防止這一點,將文件創建掩模清除:
8. 處理SIGCHLD信號
處 理SIGCHLD信號並不是必須的。但對於某些進程,特別是服務器進程往往在請求到來時生成子進程處理請求。如果父進程不等待子進程結束,子進程將成為僵 屍進程(zombie)從而占用系統資源。如果父進程等待子進程結束,將增加父進程的負擔,影響服務器進程的並發性能。在Linux下可以簡單地將 SIGCHLD信號的操作設為SIG_IGN。
這樣,內核在子進程結束時不會產生僵屍進程。這一點與BSD4不同,BSD4下必須顯式等待子進程結束才能釋放僵屍進程。
以下講的是linux下如何用daemon來實現守護程序, 轉自:http://sirius.gnu.blog.163.com/blog/static/14683368020103733027574/
以前我們在看《unix環境高級編程》的時候,有專門的整章詳細介紹如何編寫一個后台daemon程序(精靈程序),主要涉及到創建會話組,切換工 作目錄,設置文件屏蔽字,關閉不必要的描述符等多個操作。這些操作對於每一個后台程序來說都是類似的。
在Linux中專門提供了一個函數來完成這個daemon化的過程,這個函數的原型如下
int daemon (int __nochdir, int __noclose);
如果__nochdir的值為0,則將切換工作目錄為根目錄;如果__noclose為0,則將標准輸入,輸出和標准錯誤都重定向到/dev /null。
經過這個函數調用后的程序將運行在后台,成為一個daemon程序,而linux下大多的服務都是以此方式運行的。
我們來看一個簡單的例子。例如編寫例子程序test.c
#include <unistd.h>
#include <stdio.h>
int do_sth()
{
//Add what u want
return 0;
}
int main()
{
daemon(0,0);
while ( 1 )
{
do_sth();
sleep(1);
}
}
編譯並運行
[leconte@localhost daemon]$ gcc -o test test.c
[leconte@localhost daemon]$ ./test
程序進入了后台,通過ps查看進程情況,可以看到進程的父進程id為1,即init進程
用lsof查看test進程所打開的文件,可以看到文件描述符0,1,2都被重定向到/dev/null
並且能夠看到,進程的當前工作目錄(cwd)為根目錄/,daemon函數已經幫我們完成了daemon化的過程,接下來我們只需要關注於程序功能 的實現了。