維基百科的解釋中: 在操作系統領域中,孤兒進程指的是在其父進程執行完成或被終止 後仍繼續運行的一類進程。 在類UNIX系統中,僵屍進程是指完成執行(通過 exit 系統調用,或運行時發生致命錯誤或收到終止信號所致)但在操作系統的進程表中仍然有一個表項(進程式控制制塊PCB),處於"終止狀態 "的進程。 ...
維基百科的解釋中:
在操作系統領域中,孤兒進程指的是在其父進程執行完成或被終止 後仍繼續運行的一類進程。
在類UNIX系統中,僵屍進程是指完成執行(通過 exit 系統調用,或運行時發生致命錯誤或收到終止信號所致)但在操作系統的進程表中仍然有一個表項(進程控制塊PCB),處於"終止狀態 "的進程。
在一個多工的電腦作業系統中,守護進程(英語:daemon,英語發音:/ˈdiːmən/或英語發音:/ˈdeɪmən/)是一種在後臺執行的電腦程式。 此類程式會被以進程的形式初始化。 守護進程程式的名稱通常以字母“d”結尾:例如,syslogd就是指管理系統日誌的守護進程。
個人理解:
1、一般情況下,子進程是由父進程創建,而子進程和父進程的退出是無順序的,兩者之間都不知道誰先退出。正常情況下父進程先結束會調用 wait 或者 waitpid 函數等待子進程完成再退出,而一旦父進程不等待直接退出,則剩下的子進程會被init(pid=1)進程接收,成會孤兒進程。(進程樹中除了init都會有父進程)。
2、如果子進程先退出了,父進程還未結束並且沒有調用 wait 或者 waitpid 函數獲取子進程的狀態信息,則子進程殘留的狀態信息( task_struct 結構和少量資源信息)會變成僵屍進程。
3、守護進程( daemon) 是指在後臺運行,沒有控制終端與之相連的進程。它獨立於控制終端,通常周期性地執行某種任務 。 守護進程脫離於終端是為了避免進程在執行過程中的信息在任何終端上顯示並且進程也不會被任何終端所產生的終端信息所打斷 。
危害:
孤兒進程結束後會被 init 進程善後,並沒有危害,而僵屍進程則會一直占著進程號,操作系統的進程數量有限則會受影響。
解決:
一般僵屍進程的產生都是因為父進程的原因,則可以通過 kill 父進程解決,這時候僵屍進程就變成了孤兒進程,被 init 進程接收
守護進程的編寫:
在不同Unix環境下,守護進程的具體編程細節並不一致。但所幸的是,守護進程的編程原則其實都一樣,區別僅在於具體的實現細節不同,這個原則就是要滿足守護進程的特性。編程規則如下:
1、在後臺運行
為避免掛起控制終端,要將 daemon 放入後臺執行,其方法是,在進程中調用fork使父進程終止,讓daemon在子進程中後臺執行。具體就是調用 fork ,然後使父進程 exit 。這樣做實現了下麵幾點:
第一,如果該精靈進程是由一條簡單s h e l l 命令起動的,那麼使父進程終止使得s h e l l 認為這條命令已經執行完成。
第二,子進程繼承了父進程的進程組I D ,但具有一個新的進程I D ,這就保證了子進程不是一個進程組的首進程。這對於下麵就要做的s e t s i d 調用是必要的前提條件。
2、脫離控制終端,登錄會話和進程組
登錄會話可以包含多個進程組,這些進程組共用一個控制終端,這個控制終端通常是創建進程的登錄終端、控制終端,登錄會話和進程組通常是從父進程繼承下來的。我們的目的就是要擺脫它們,使之不受它們的影響。
其方法是在第一點的基礎上,調用setsid()使進程成為會話組長:
需要說明的是,當進程是會話組長時,setsid()調用會失敗,但第一點已經保證進程不是會話組長。setsid()調用成功後,進程成為新的會話組長和新的進程組長,並與原來的登錄會話和進程組脫離,由於會話過程對控制終端的獨占性,進程同時與控制終端脫離。
具體是操作就是:
(a )成為新對話期的首進程
(b )成為一個新進程組的首進程
(c )沒有控制終端。
3、禁止進程重新打開控制終端(fork第二次的原理)
現在,進程已經成為無終端的會話組長,但它可以重新申請打開一個控制終端。可以通過使進程不再成為會話組長來禁止進程重新打開控制終端:
4、關閉打開的文件描述符
進程從創建它的父進程那裡繼承了打開的文件描述符。如不關閉,將會浪費系統資源,造成進程所在地文件系統無法卸下以及無法預料的錯誤。一般來說,必要的是關閉0、1、2三個文件描述符,即標準輸入、標準輸出、標準錯誤。因為我們一般希望守護進程自己有一套信息輸出、輸入的體系,而不是把所有的東西都發送到終端屏幕上。調用fclose();
5、改變當前工作目錄
將當前工作目錄更改為根目錄。從父進程繼承過來的當前工作目錄可能在一個裝配的文件系統中。因為精靈進程通常在系統再引導之前是一直存在的,所以如果精靈進程的當前工作目錄在一個裝配文件系統中,那麼該文件系統就不能被拆卸。
另外,某些精靈進程可能會把當前工作目錄更改到某個指定位置,在此位置做它們的工作。例如,行式印表機假離線精靈進程常常將其工作目錄更改到它們的s p o o l 目錄上。
可以調用chdir(“目錄”);
6、重設文件創建掩碼
將文件方式創建屏蔽字設置為0 。由繼承得來的文件方式創建屏蔽字可能會拒絕設置某些許可權。例如,若精靈進程要創建一個組可讀、寫的文件,而繼承的文件方式創建屏蔽字,屏蔽了這兩種許可權,則所要求的組可讀、寫就不能起作用。
7、處理SIGCHLD 信號
處理SIGCHLD信號並不是必需的。但對於某些進程,特別是伺服器進程往往在請求到來時生產子進程出來請求。如果父進程不等待子進程結束,子進程將成為僵屍進程,(zombie)而仍占用系統資源。如果父進程等待子進程結束,將增加父進程的負擔,影響伺服器進程的併發性能。在系統V下可以簡單的將SIGCHLD信號的操作設為SIG-IGN:
signal(SIGCHLD,SIG_IGN);
這樣,內核在子進程結束時不會產生僵屍進程,這一點與BSD4不同,在BSD4下必須顯示等 待子進程結束才能釋放僵屍進程。
python 版本代碼
def initDaemon(stdoutfd, stderrfd=None, basePath=None): """ 初始化成daemon進程 """ basePath = '/' if basePath is None else basePath curTimeStr = time.strftime("%Y-%m-%d %H:%M:%S") try: stdoutfd.write("Start on %s, " % curTimeStr) stdoutfd.flush() except: raise try: if os.fork() > 0: os._exit(0) except OSError: raise OSError("fork error") os.chdir(basePath) os.setsid() sys.stdout = stdoutfd sys.stderr = stdoutfd if stderrfd is None else stderrfd sys.stdin = open("/dev/null", 'r') try: if os.fork() > 0: os._exit(0) except OSError: raise OSError("fork 2 error") pid = int(os.getpid()) stdoutfd.write("pid=%s\n" % pid) stdoutfd.flush() return