一,守護進程概述
Linux Daemon(守護進程)是運行在後臺的一種特殊進程。它獨立於控制終端並且週期性地執行某種任務或等待處理某些發生的事件。它不需要用戶輸入就能運行而且提供某種服務,不是對整個系統就是對某個用戶程序提供服務。Linux系統的大多數服務器就是通過守護進程實現的。常見的守護進程包括系統日誌進程syslogd、 web服務器httpd、郵件服務器sendmail和數據庫服務器mysqld等。
守護進程一般在系統啓動時開始運行,除非強行終止,否則直到系統關機都保持運行。守護進程經常以超級用戶(root)權限運行,因爲它們要使用特殊的端口(1-1024)或訪問某些特殊的資源。
一個守護進程的父進程是init進程,因爲它真正的父進程在fork出子進程後就先於子進程exit退出了,所以它是一個由init繼承的孤兒進程。守護進程是非交互式程序,沒有控制終端,所以任何輸出,無論是向標準輸出設備stdout還是標準出錯設備stderr的輸出都需要特殊處理。
守護進程的名稱通常以d結尾,比如sshd、xinetd、crond等。在linux系統中也稱爲service。CentOS7提供了一整套的service管理,systemctl命令。
二,創建守護進程步驟
首先我們要了解一些基本概念:
- 進程組 :
每個進程也屬於一個進程組
每個進程主都有一個進程組號,該號等於該進程組組長的PID號 .
一個進程只能爲它自己或子進程設置進程組ID號 - 會話期:
會話期(session)是一個或多個進程組的集合。
setsid()函數可以建立一個對話期:
如果,調用setsid的進程不是一個進程組的組長,此函數創建一個新的會話期。
(1)此進程變成該對話期的首進程
(2)此進程變成一個新進程組的組長進程。
(3)此進程沒有控制終端,如果在調用setsid前,該進程有控制終端,那麼與該終端的聯繫被解除。 如果該進程是一個進程組的組長,此函數返回錯誤。
(4)爲了保證這一點,我們先調用fork()然後exit(),此時只有子進程在運行
現在我們來給出創建守護進程所需步驟:
編寫守護進程的一般步驟步驟:
(1)在父進程中執行fork並exit推出;
(2)在子進程中調用setsid函數創建新的會話;
(3)在子進程中調用chdir函數,讓根目錄 ”/” 成爲子進程的工作目錄;
(4)在子進程中調用umask函數,設置進程的umask爲0;
(5)在子進程中關閉任何不需要的文件描述符
說明:
1. 在後臺運行。
爲避免掛起控制終端將Daemon放入後臺執行。方法是在進程中調用fork使父進程終止,讓Daemon在子進程中後臺執行。
if(pid=fork())
exit(0);//是父進程,結束父進程,子進程繼續
2. 脫離控制終端,登錄會話和進程組
有必要先介紹一下Linux中的進程與控制終端,登錄會話和進程組之間的關係:進程屬於一個進程組,進程組號(GID)就是進程組長的進程號(PID)。登錄會話可以包含多個進程組。這些進程組共享一個控制終端。這個控制終端通常是創建進程的登錄終端。
控制終端,登錄會話和進程組通常是從父進程繼承下來的。我們的目的就是要擺脫它們,使之不受它們的影響。方法是在第1點的基礎上,調用setsid()使進程成爲會話組長:
setsid();
說明:當進程是會話組長時setsid()調用失敗。但第一點已經保證進程不是會話組長。setsid()調用成功後,進程成爲新的會話組長和新的進程組長,並與原來的登錄會話和進程組脫離。由於會話過程對控制終端的獨佔性,進程同時與控制終端脫離。
3. 禁止進程重新打開控制終端
現在,進程已經成爲無終端的會話組長。但它可以重新申請打開一個控制終端。可以通過使進程不再成爲會話組長來禁止進程重新打開控制終端:
if(pid=fork())
exit(0);//結束第一子進程,第二子進程繼續(第二子進程不再是會話組長)
4. 關閉打開的文件描述符
進程從創建它的父進程那裏繼承了打開的文件描述符。如不關閉,將會浪費系統資源,造成進程所在的文件系統無法卸下以及引起無法預料的錯誤。按如下方法關閉它們:
for(i=0;i 關閉打開的文件描述符close(i);>
5. 改變當前工作目錄
進程活動時,其工作目錄所在的文件系統不能卸下。一般需要將工作目錄改變到根目錄。對於需要轉儲核心,寫運行日誌的進程將工作目錄改變到特定目錄如/tmpchdir("/")
- 重設文件創建掩模
進程從創建它的父進程那裏繼承了文件創建掩模。它可能修改守護進程所創建的文件的存取位。爲防止這一點,將文件創建掩模清除:umask(0); - 處理SIGCHLD信號
處理SIGCHLD信號並不是必須的。但對於某些進程,特別是服務器進程往往在請求到來時生成子進程處理請求。如果父進程不等待子進程結束,子進程將成爲殭屍進程(zombie)從而佔用系統資源。如果父進程等待子進程結束,將增加父進程的負擔,影響服務器進程的併發性能。在Linux下可以簡單地將SIGCHLD信號的操作設爲SIG_IGN。
signal(SIGCHLD,SIG_IGN);
這樣,內核在子進程結束時不會產生殭屍進程。這一點與BSD4不同,BSD4下必須顯式等待子進程結束才能釋放殭屍進程。
deamon進程的標準實行,關閉了標準輸入輸出後,重新打開了/dev/null,就是黑洞,然後dup(0),dup(0),相當於將fd=0,1,2,都指向了黑洞
示例代碼
/*
* Fork a child process and then kill the parent so make the calling
* program a daemon process.
*/
void makedaemon (void)
{
if (fork () != 0)
exit (0);
setsid ();
set_signal_handler (SIGHUP, SIG_IGN);
if (fork () != 0)
exit (0);
if (chdir ("/") != 0) {
log_message (LOG_WARNING,
"Could not change directory to /");
}
umask (0177);
#ifdef NDEBUG
/*
* When not in debugging mode, close the standard file
* descriptors.
*/
close (0);
close (1);
close (2);
#endif
}