小說python中的孤兒進程
殭屍進程,大家都會對其嗤之以鼻,敬而遠之,畢竟臭名在外。
孤兒進程,大家對其都很寬容,甚至可以說是放縱,只因系統會收留。
然而,在實際應用中,孤兒進程雖然不會給系統造成直接性的危害,但更多時候會對業務造成一些影響,如當子進程為一個基於tcp的socket服務時,會造成主進程再次啟動時無法啟動,埠被佔用。主進程退出了,子進程會因為無法獲得某些資源,而變成業務上的"殭屍進程",這實際也是資源浪費。對於一些有進程監控的服務來說,可能會造成業務主服務無法重啟,或是進程不可控。
鑒於這些情況下,很多時候是不希望產生孤兒進程的,子進程應隨父進程結束而結束。
本文就小說一把如何做一個有擔當的"父親",不要不負責任的"一走了之",隨意丟棄自己的"孩子們"。
什麼是孤兒進程
孤兒進程:一個父進程退出,而它的一個或多個子進程還在運行,那麼那些子進程將成為孤兒進程。孤兒進程將被init進程(進程號為1)所收養,並由init進程對它們完成中止後的資源回收工作
通過下面的具體例子,具體看看
centralized_in_out服務會啟動8個子進程,父進程ID為5310,子進程ID為5312-5319
將父進程(5310)kill掉,可以看到子進程5312-5319全由ID為1的進程接管
如何做
上面看到子進程5312-5319被init進程接管了,但這不是我想要的結果,當前業務中,會再次拉起centralized_in_out服務,會再啟動8個子進程,這樣進程數太多,會失控,不符合業務需求。
我需要的是」父子共進退「,如何做呢?
豆瓣的工程師們,已經給出了解決辦法,具體參見:
https://github.com/douban/CaoE
修改代碼,用起來,效果如下
GIF
為什麼
豆瓣工程師給出了解決辦法,不能只拿來用用,得問幾個為什麼?通過什麼實現的?為什麼要這麼做呢?
下面具體分析下實現方法:
1. 方法概述
實現思路是通過創建一個子進程和孫子進程,子進程會監控父進程的狀態,當檢測到父進程退出後,會給進程組發送信號通知殺死孫子進程及其子進程。
這裡涉及到進程組和信號兩個重要概念,下面具體闡述。
2. 概念闡述
進程組:每個進程都會屬於一個進程組(process group),每個進程組中可以包含多個進程。進程組會有一個領導進程 (process group leader),領導進程的PID成為進程組的ID (process group ID, PGID),用來標識進程組。
如下圖所示,centralized_in_out服務父進程的ID為5538(它的PGID為5538),子進程ID為5540(它的PGID為5540),孫子進程的ID為5541(它的PGID為5540),孫孫進程5542-5549的PGID都為5541
信號:具體概念這裡不多說了,有些大,而且晦澀難懂。主要涉及信號定義和處理函數的註冊綁定,後面結合代碼具體說明
3. 實現詳解
通過兩次fork,創建子進程(ID:5540)和孫進程(ID:5541),
其中子進程中有重要的一步,os.setpgrp()將子進程的進程組ID(5540)設為當前進程組的ID,後面孫進程和孫孫進程的進程組ID都為5540。
子進程在exit_when_parent_or_child_dies方法中循環等待父進程狀態,當PPID為1時,說明父進程已退出,通過killpg()將進程組中的所有進程(孫孫進程)殺死,然後自己退出。
到此,整個流程就清晰了,通過設置孫進程和孫孫進程的進程組ID為子進程的進程ID,當主進程退出,子進程被init進程接管時,通過killpg將同一個進程組ID的孫進程和孫孫進程中止。
但如果仔細看代碼,
exit_when_parent_or_child_dies方法中:
if os.getppid() == 1: 永遠執行不到,因為父進程退出時,捕獲如下信號
而這些信號處理方法中都會通過killpg殺死進程組,子進程也屬於這個進程組,也會被kill掉。
TAG:chafezhou |