【一】僵尸進程和孤兒進程
【1】引入
-
我們知道在unix/linux中,正常情況下,子進程是通過父進程創建的,子進程在創建新的進程。
-
子進程的結束和父進程的運行是一個異步過程,即父進程永遠無法預測子進程 到底什么時候結束。
-
當一個 進程完成它的工作終止之后,它的父進程需要調用wait()或者waitpid()系統調用取得子進程的終止狀態。
【2】僵尸進程
(1)什么是僵尸進程
僵尸進程是指完成自己的任務之后,沒有被父進程回收資源
例如:開了一個pycharm ---> 將 pycharm 關閉 --> 任務管理器所有的關于 pycharm 的進程應該都被關閉,但是有時候會發現,有某幾個 pycharm 進程任然在后臺運行 --> 本來這部分資源應該被回收,結果因為還在運行,占用系統資源
(2)解決辦法
-
因此,UNⅨ提供了一種機制可以保證父進程可以在任意時刻獲取子進程結束時的狀態信息
-
-
在每個進程退出的時候,內核釋放該進程所有的資源,包括打開的文件,占用的內存等。
-
-
-
-
但是仍然為其保留一定的信息(包括進程號the process ID,退出狀態the termination status of the process,運行時間the amount of CPU time taken by the process等)
-
-
-
-
直到父進程通過wait / waitpid來取時才釋放.
-
-
-
-
但這樣就導致了問題,如果進程不調用wait / waitpid的話,那么保留的那段信息就不會釋放,其進程號就會一直被占用,但是系統所能使用的進程號是有限的
-
如果大量的產生僵死進程,將因為沒有可用的進程號而導致系統不能產生新的進程. 此即為僵尸進程的危害,應當避免。
-
-
-
任何一個子進程(init除外)在exit()之后,并非馬上就消失掉,而是留下一個稱為僵尸進程(Zombie)的數據結構,等待父進程處理。
-
-
這是每個子進程在結束時都要經過的階段。
-
如果子進程在exit()之后,父進程沒有來得及處理,這時用ps命令就能看到子進程的狀態是“Z”。
-
如果父進程能及時 處理,可能用ps命令就來不及看到子進程的僵尸狀態,但這并不等于子進程不經過僵尸狀態。
-
如果父進程在子進程結束之前退出,則子進程將由init接管。
-
init將會以父進程的身份對僵尸狀態的子進程進行處理。
-
(3)示例:
import multiprocessing import time ? ? def work(name):print(f'{name} is starting ...')time.sleep(2)print(f'{name} is ending ...\n') ? ? def main():for i in range(3):task = multiprocessing.Process(target=work,args=(f'task_{i}',))task.start() ? ? if __name__ == '__main__':print(f'main process starting ...')main()print(f'main process ending ...') ? ? # main process starting ... # main process ending ... # task_0 is starting ... # task_1 is starting ... # task_2 is starting ... # task_0 is ending ... # task_1 is ending ... # task_2 is ending ...
【3】孤兒進程
(1)什么是孤兒進程
孤兒進程是指父進程在子進程終止之前就退出了
例如:在沒有關閉共享屏幕的前提下,直接退出騰旭會議,這時共享屏幕也會退出,不會還掛在后臺
在主程序死亡以后,因為子進程就沒有辦法再和主進程通信。就像是有一個福利院 init進程 會接管這些沒有父親的孤兒,如果最后沒有人來領取嗎就把所有子線程殺死
(2)示例:
import os import sys import time ? ? if __name__ == '__main__':pid = os.getpid()ppid = os.getppid()print('父進程:','pid:',pid,'ppid:',ppid)pid = os.fork()# 執行pid=os.fork()則會生成一個子進程# 返回值pid有兩種值:# 如果返回的pid值為0,表示在子進程中# 如果返回的pid值>0,表示在父進程中if pid > 0:print('父進程終止')sys.exit(0)# 保證主線程退出完畢time.sleep(1)print('我是子進程:',os.getpid(),os.getppid())
【4】僵尸進程和孤兒進程誰的危害性更大?
-
僵尸進程的危害性更大,因為僵尸進程一直會持續存在,在后臺占用資源,孤兒進程會因為主進程的死亡,然后有init進程接管,并銷毀
例如:有個進程,它定期的產 生一個子進程,這個子進程需要做的事情很少,做完它該做的事情之后就退出了,因此這個子進程的生命周期很短,但是 父進程只管生成新的子進程,至于子進程 退出之后的事情,則一概不聞不問,這樣的話系統運行一段后就會存在很多僵尸進程,如果實在Linux中的話可以使用ps命令查看,就可以看到很多狀態為Z的進程
嚴格來說,僵尸進程并不是問題的根源,罪魁禍首是產生大量僵尸進程的父進程,因此,當我們需要消滅系統中大量的僵尸進程時,最好的辦法就是將制造的元兇干掉(也就是通過kill發送SIGTERM或者SIFKILL信號),如此的話由他產生的僵尸進程就變成了孤兒進程,就會被init接管,然后wait()這些孤兒進程,即釋放了
【二】守護進程
【1】什么是守護進程
-
守護進程時一種特殊的進程,只要系統不關機,就會一直存在
-
它們通常不與用戶直接交互,也不接受標準輸入和輸出,而是在后臺執行某種任務或提供某種服務。
-
守護進程往往是由系統管理員手動啟動的,它們可以在系統啟動時自動啟動,一直運行在后臺,直到系統關閉或被停止,而且不需要用戶交互有較高的權限,因此編寫守護進程需要注意安全性和穩定性
-
常見的守護進程包括網絡服務(如web服務器、郵件服務器、ftp服務器)、日志記錄系統
-
例如:安裝一個MySQL服務(TCP服務端),關機會導致服務端關閉,MySQL服務加一個守護進程,只要系統不關機,MySQL服務就會一直在后臺運行,除非主動殺死
示例:
from multiprocessing import Process import time ? ? def task(name):print(f'皇太妃 :>>{name}>>正常存活')time.sleep(2)print(f'皇太妃 :>>{name}>>正常死亡') ? ? if __name__ == '__main__':print(f'皇帝 :>>> ChiMeng >>> 執掌江山')p = Process(target=task, args=('dream',)) ?p.start() ?print(f'皇帝 :>>> ChiMeng >>> 壽終正寢')# 主進程結束,子進程未結束# 皇帝 :>>> ChiMeng >>> 執掌江山 # 皇帝 :>>> ChiMeng >>> 壽終正寢 # 皇太妃 :>>dream>>正常存活 # 皇太妃 :>>dream>>正常死亡