在Linux/UNIX系統引導的時候會開啓不少服務,這些服務稱爲守護進程(也叫Daemon進程)。守護進程是脫離於控制終端而且在後臺週期性地執行某種任務或等待處理某些事件的進程,脫離終端是爲了不進程在執行過程當中的信息在任何終端上顯示而且進程也不會被任何終端所產生的中斷信息所終止。html
建立守護進程的通常步驟:linux
(1) 建立子進程,退出父進程shell
爲了脫離控制終端須要退出父進程,以後的工做都由子進程完成。在Linux中父進程先於子進程退出會形成子進程成爲孤兒進程,而每當系統發現一個孤兒進程時,就會自動由1號進程(init)收養它,這樣,原先的子進程就會變成init進程的子進程。編程
ps –ef | grep ProcName 經過PID/PPID查看進程的父子關係服務器
(2) 在子進程中建立新的會話session
使用系統函數setsid來完成。併發
man 2 setsid 查看關於setsid函數的說明less
setsid – creates a session and sets theprocess group ID函數
#include <unistd.h>oop
pid_t setsid(void);
setsid() creates a new session if thecalling process is not a process group leader. The calling process is theleader of the new session, the process group leader of the new process group,and has no controlling tty. The process group ID and session ID of the callingprocess are set to the PID of the calling process. The calling process will bethe only process in this new process group and in this new session.
進程組:是一個或多個進程的集合。進程組有進程組ID來惟一標識。除了進程號PID以外,進程組ID也是一個進程的必備屬性。每一個進程組都有一個組長進程,其組長進程的進程號等於進程組ID,且該進程組ID不會因組長進程的退出而受到影響。
setsid函數做用:用於建立一個新的會話,並擔任該會話組的組長。調用setsid有3個做用
(a) 讓進程擺脫原會話的控制;
(b) 讓進程擺脫原進程組的控制;
(c) 讓進程擺脫原控制終端的控制;
使用setsid函數的目的:因爲建立守護進程的第一步調用了fork函數來建立子進程再將父進程退出。因爲在調用fork函數時,子進程拷貝了父進程的會話期、進程組、控制終端等,雖然父進程退出了,但會話期、進程組、控制終端等並無改變,所以,這還不是真正意義上的獨立開了。使用setsid函數後,可以使進程徹底獨立出來,從而擺脫其餘進程的控制。
介紹一下Linux中的進程與控制終端,登陸會話和進程組之間的關係:進程屬於一個進程組,進程組號(GID)就是進程組長的進程號 (PID)。登陸會話能夠包含多個進程組。這些進程組共享一個控制終端。這個控制終端一般是建立進程的登陸終端。 控制終端,登陸會話和進程組一般是從父進程繼承下來的。咱們的目的就是要擺脫它們,使之不受它們的影響。方法是在第1點的基礎上,調用setsid()使 進程成爲會話組長.當進程是會話組長時setsid()調用失敗。但第一點已經保證進程不是會話組長。setsid()調用成功後,進程成爲新的會話組長和新的進程組長,並與原來的登陸會話和進程組脫離。因爲會話過程對控制終端的獨佔性,進程同時與控制終端脫離。
(3) 改變當前目錄爲根目錄
使用fork建立的子進程繼承了父進程的當前的工做目錄。因爲在進程運行中,當前目錄所在的文件系統是不能卸載的,這對之後的使用會形成諸多的麻煩。所以,一般的作法是讓根目錄」/」做爲守護進程的當前工做目錄。這樣就能夠避免上述的問題。若有特殊的需求,也能夠把當前工做目錄換成其餘的路徑。改變工做目錄的方法是使用chdir函數。
(4) 重設文件權限掩碼
文件權限掩碼:是指屏蔽掉文件權限中的對應位。例如,有個文件權限掩碼是050,它就屏蔽了文件組擁有者的可讀與可執行權限(對應二進制爲,rwx, 101)。因爲fork函數建立的子進程繼承了父進程的文件權限掩碼,這就給子進程使用文件帶來了諸多的麻煩。所以,把文件權限掩碼設置爲0(即,不屏蔽任何權限),能夠加強該守護進程的靈活性。設置文件權限掩碼的函數是umask。一般的使用方法爲umask(0)。
(5) 關閉文件描述符
用fork建立的子進程也會從父進程那裏繼承一些已經打開了的文件。這些被打開的文件可能永遠不會被守護進程讀寫,但它們同樣消耗系統資源,並且可能致使所在的文件系統沒法卸載。在使用setsid調用以後,守護進程已經與所屬的控制終端失去了聯繫,所以從終端輸入的字符不可能達到守護進程,守護進程中用常規方法(如printf)輸出的字符也不可能在終端上顯示出來。因此,文件描述符爲0、一、2(即,標準輸入、標準輸出、標準錯誤輸出)的三個文件已經失去了存在的價值,也應該關閉。
(6) 守護進程退出處理
當用戶須要外部中止守護進程時,一般使用kill命令中止該守護進程。因此,守護進程中須要編碼來實現kill發出的signal信號處理,達到進程正常退出。
這篇文章主要介紹了C語言編寫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. 在後臺運行。
爲避免掛起控制終端將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(「/」)
6. 重設文件建立掩模
進程從建立它的父進程那裏繼承了文件建立掩模。它可能修改守護進程所建立的文件的存取位。爲防止這一點,將文件建立掩模清除:umask(0);
7. 處理SIGCHLD信號
處理SIGCHLD信號並非必須的。但對於某些進程,特別是服務器進程每每在請求到來時生成子進程處理請求。若是父進程不等待子進程結束,子進程將成爲 殭屍進程(zombie)從而佔用系統資源。若是父進程等待子進程結束,將增長父進程的負擔,影響服務器進程的併發性能。在Linux下能夠簡單地將 SIGCHLD信號的操做設爲SIG_IGN。
signal(SIGCHLD,SIG_IGN);
這樣,內核在子進程結束時不會產生殭屍進程。這一點與BSD4不一樣,BSD4下必須顯式等待子進程結束才能釋放殭屍進程。
下面是一個簡單的實現:
參考:
[1] 守護進程
[2] 編寫Linux/Unix守護進程
[3] Linux 信號signal處理函數
[4] The usage of sig_atomic_t in linux signal mask function