守护进程和inetd守护进程
一、守護進程介紹
守護進程是在后臺運行且不與任何控制終端相關聯的進程。通常由系統初始化腳本啟動,當然也可以在shell提示符下用命令行啟動,不過這種守護進程必須親自脫離于控制終端的關聯。
守護進程的啟動方法有:
1、系統初始化階段,由系統初始化腳本啟動。這些腳本通常位于/etc、/etc/rc開頭的某個目錄中。由這些腳本啟動的守護進程從一開始就有root特權。例如:inetd超級服務器、Web服務器、郵件服務器、syslogd守護進程等都用這種方式啟動。
???????注:系統初始化的時候,系統內運行著一些守護進程(syslogd?inetd? ......)
2、靠inetd超級服務器啟動。inetd超級服務器監聽網絡請求,每當有一個請求到達時,啟動相應的實際服務器。
3、corn守護進程按照規則定期執行一些程序,由它啟動執行的程序同樣作為守護進程運行。Corn自身由第一種方式啟動。
4、at命令用于指定將來某個時刻的程序執行。
5、從用戶終端或者后臺啟動。這么做往往是為了測試守護程序或重啟因某種原因而終止的守護進程。
二、syslogd守護進程
syslogd守護進程由系統初始化腳本啟動,在系統工作期間一直運行。步驟如下:
1、讀取配置文件;
2、創建一個數據報套接字,綁定/var/run/log
3、創建一個UDP套接字,綁定端口514
4、打開路徑/dev/klog。
此后便一直運行,調用select等待它的3個描述符之一變成可讀,然后讀入日志消息,按照配置文件進行處理。(收到SIGHUP信號,重新讀取配置文件)。
?????????注:其他的進程(例如由inetd創建的守護進程)將自己所產生的輸出通過調用syslog函數發送給syslogd進程,這些消息有lever和facility,系統管理員根據嚴重級別,做出不同的消息處理方式。
由于守護進程沒有終端,所以它的消息用fprintf到stderr上,從守護進程登記消息使用syslog函數:
void syslog(int priority,const char *message,…);
三、如何把一個普通進程轉變成守護進程?
使用daemon函數或者daemon_init函數。
編程要點:
1.?在后臺運行?
?? 為避免掛起控制終端將Daemon放入后臺執行。方法是在進程中調用fork使父進程終止,讓Daemon在子進程中后臺執行。?
if(pid=fork()) exit(0);??? //是父進程,結束父進程,子進程繼續?
2.?脫離控制終端,登錄會話和進程組?
??? 有必要先介紹一下Linux中的進程與控制終端,登錄會話和進程組之間的關系:進程屬于一個進程組,進程組號(GID)就是進程組長的進程號(PID)。登錄會話可以包含多個進程組。這些進程組共享一個控制終端。這個控制終端通常是創建進程的登錄終端??刂平K端,登錄會話和進程組通常是從父進程繼承下來的。我們的目的就是要擺脫它們 ,使之不受它們的影響。方法是在第1點的基礎上,調用setsid()使進程成為會話組長:?
setsid();?
??? 說明:當進程是會話組長時setsid()調用失敗。但第一點已經保證進程不是會話組長。setsid()調用成功后,進程成為新的會話組長和新的進程組長,并與原來的登錄會話和進程組脫離。由于會話過程對控制終端的獨占性,進程同時與控制終端脫離。?
3.?禁止進程重新打開控制終端?
??? 現在,進程已經成為無終端的會話組長。但它可以重新申請打開一個控制終端??梢酝ㄟ^使進程不再成為會話組長來禁止進程重新打開控制終端:?
if(pid=fork()) exit(0);? //結束第一子進程,第二子進程繼續(第二子進程不再是會話組長)?
4.?關閉打開的文件描述符?
??? 進程從創建它的父進程那里繼承了打開的文件描述符。如不關閉,將會浪費系統資源,造成進程所在的文件系統無法卸下以及引起無法預料的錯誤。按如下方法關閉它們:?
for(i=0;i?關閉打開的文件描述符close(i);
5.?改變當前工作目錄?
??? 進程活動時,其工作目錄所在的文件系統不能卸下。一般需要將工作目錄
改變到根目錄 。對于需要轉儲核心,寫運行日志的進程將工作目錄改變到特定目錄如/tmp
chdir("/")?
6.?重設文件創建掩模?
?? 進程從創建它的父進程那里繼承了文件創建掩模。它可能修改守護進程所創建的文件的存取位。為防止這一點,將文件創建掩模清除:
umask(0);?
7.?處理SIGCHLD信號?
處理SIGCHLD信號并不是必須的。但對于某些進程,特別是服務器進程往往在請求到來時生成子進程處理請求。如果父進程不等待子進程結束,子進程將成為僵尸進程(zombie )從而占用系統資源。如果父進程等待子進程結束,將增加父進程的負擔,影響服務器進程的并發性能。在Linux下可以簡單地將SIGCHLD信號的操作設為SIG_IGN。?
signal(SIGCHLD,SIG_IGN);?
這樣,內核在子進程結束時不會產生僵尸進程。這一點與BSD4不同,BSD4下必須顯式等待子進程結束才能釋放僵尸進程。?
四、inetd守護進程
典型的unix系統可能存在許多服務器,它們只是等待客戶請求的到達,如FTP、Telnet、Rlogin、TFTP等。最開始,所有的這些服務都與一個進程相關聯,這些進程在系統啟動時開始運行,而且執行幾乎相同的啟動任務:創建一個套接口,把本服務的眾所周知的端口捆綁到該套接口,等待一個連接(TCP)或是一個數據報(UDP),然后派生子進程。子進程為客戶提供服務,父進程則等待下一個客戶請求。這個模型存在兩個問題:
1.??所有這些守護進程含有幾乎相同的啟動代碼(套接口的創建以及成為守護進程)。
2.??每個守護進程在進程表中占據一項,并且大部分時間處于睡眠狀態。
inetd超級服務器使上述的問題得到簡化:
1.?通過inetd處理普通進程的大部分細節以簡化守護程序的編寫。
2.?單個進程(inetd)就能為多個服務等待外來的客戶請求,以此取代每個服務一個進程的做法,減少了系統中的進程數。
initd守護進程的工作流程:
1,在啟動階段,讀入/etc/inetd.conf文件并給該文件中指定的每個服務創建一個適當類型的套接口。
2,為每個套接口調用bind,指定捆綁相應服務器的眾所周知端口和通配IP地址。
3,對于每個TCP套接口,調用listen以接受外來的連接請求。對于數據報套接口則不執行本步驟。
4,創建完畢所有套接口后,調用select等待其中任何一個套接口變為可讀。
5,當select返回指出某個套接口已可讀之后,如果該套接口是一個TCP套接口,而且其服務器的wait-flag值為nowait,那就調用accept接受這個新連接。
6,inetd守護進程調用fork派生進程,并由子進程處理服務請求。
7,如果第5步中select返回的是一個字節流套接口,那么父進程必須關閉接受了的已連接套接口,父進程再次調用select,等待下一個變為可讀的套接口。
如果5)中,其服務器的wait-flag值為wait的話,對于數據報服務,父進程應該禁止相應的套接字(通過關閉select描述符集合中對應的位),防止select再次返回可讀條件。這就意味著子進程接管了這個套接字直到自身終止為止。一旦子進程自身終止后,父進程就會被通知一個sigchld信號,而父進程此時就會獲得子進程的pid。父進程通過再次打開相應的套接字的位,使得該套接字再次成為select的候選套接字。
那么,為什么要這么做呢?
原因其實是,數據報服務器只有一個套接字,它不像tcp有監聽套接字也有連接套接字。如果inetd不關閉對于某個數據報套接字的可檢查條件,而且父進程有又是先于子進程執行,那么引發本次fork的那個數據報仍然在套接字接收緩沖區,導致select再次返回可讀條件,導致inetd進程再次fork另一個不必要的子進程。(UNP?p297頁)
總結
以上是生活随笔為你收集整理的守护进程和inetd守护进程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 普通进程的守护进程化
- 下一篇: select/poll/epoll 与