僵屍進程、孤兒進程、守護進程、進程組、會話、前台進程組、后台進程組
1,僵屍進程
子進程結束,父進程沒有明確的答復操作系統內核:已收到子進程結束的消息。此時操作系統內核會一直保存該子進程的部分PCB信息,同時將進程的狀態置為defunct--->僵屍進程
通過ps -ef | grep "xxx"找到僵屍進程的PID,通過kill -9 PID,也不能強制殺死;即僵屍進程是不能夠被直接消除掉的
僵屍進程的危害:占用PCB資源(主要是PID資源),系統將會因為產生大量的僵屍進程,而沒有可用的進程號來產生新進程,導致系統資源不足
僵屍進程的三種解決方案:
核心思想:父進程的知道子進程的結束,並且明確的回復操作系統,此時操作系統才能回收PCB資源,避免僵屍進程的產生
此時父進程需要處理子進程的結束,需要調用wait()、waitpid()函數族;wait()會等待子進程的結束,調用wait()的進程會阻塞,直到接收到子進程的消息才被喚醒;子進程結束時,會給父進程發送SIGCHLD(17)信號
(1)、wait()用的場合:只能在父子進程中,父進程等待子進程的結束,一直在阻塞,信號通知父進程,避免了子進程的僵屍狀態
wait()方法只能在具有子進程的進程中使用,如果一個進程沒有子進程,調用wait()方法會失敗
wait()方法是用於處理子進程結束狀態的,同時讓父進程明確告知操作系統內核已經處理了子進程的結束,從而使得操作系統能夠徹底回收子進程的PCB信息
(2)、:將僵屍進程的父進程exit()結束掉,此時僵屍進程被init進程接收;init進程會查看進程狀態,發現是僵屍進程,就會通知操作系統將其回收
(3)、:寫一個信號處理函數,signal(SIGCHLD, 函數指針),在信號處理函數中調用wait()即可,此時父進程就會知道子進程結束,會明確的回復操作系統(就是當子進程結束時,信號處理函數接收到SIGCHLD信號時,方才調用wait(),此時父進程才阻塞)
以上的解決方案:
方案一:使父進程長時間的阻塞
方案二:使父進程必須的結束掉
方案三:即可解決父進程的阻塞,又可避免僵屍進程的產生
2,孤兒進程
當父進程退出,而它的一個或多個子進程還在運行,那么那些子進程將成為孤兒進程,由於進程不可能脫離進程樹而獨立存在,孤兒進程將被init進程(進程號為1)所收養,並由init進程對它們完成狀態收集工作。孤兒進程被收養后進行正常的釋放,沒有危害
3,守護(精靈)進程
守護一個服務,長期駐留在內存中提供服務,不能夠受制於終端;
如何讓一個進程成為守護進程?
讓一個進程脫離前台進程組關系,如下2步:i、創建一個新的會話;ii、構成孤兒進程
進程組:
在Linux下進程除了具有父子關系這樣的組織關系以外,還具有分組的組織關系。任何一個進程都需要隸屬於某個進程組
每一個進程組都擁有一個獨立的進程組編號,可以通過getpgid()方法得到。
每一個進程組都擁有且只擁有一個組長進程。可以通過該組長管理其組內的其他進程的統一行為。(例如:組長進程若獲取一個特殊的信號,該信號可以傳遞給組內所有進程)
進程組ID就是進程組組長的進程ID
進程組內的成員都是組長的子及子孫
創建:setpgid()
會話:
是進程組之間的某種關系,即每一個進程除了要歸屬於一個進程組以外,還需要歸屬於一個會話之中,會話的概念主要是從終端登陸到計算機之后得到的
當一個終端登錄到計算機之后,為了方便將不同的終端隔離開,同時又能夠充分合理的管理一個終端下產生的所有進程,因此而提出了會話的概念。換句不准確描述來說:會話就是用戶登錄之后從登錄服務進程到shell進程所組成的集合
一個會話通常是由多個進程組組成的,分為兩部分(前台進程組,后台進程組)。
會話具有一個會話首進程。操作系統通過會話的首進程來管理整個會話中的所有進程組。
創建會話:setsid()方法來創建一個新的會話,從而就會出現新的進程組
前台進程組:
前台進程組是和終端服務進程、bash進程捆綁在一起的,和終端直接相關。那么,終端的任何操作都會影響到所有的前台進程組
在shell交互環境中執行一個命令,就會產生一個新的進程來執行這個命令,不僅如此,還會產生一個新的進程組,該進程組的組長就是被執行命令而形成的新進程
前台進程組的最大問題就在於:由終端控制,終端只需要讓進程組的組長停止,其組內的所有進程都會停止
后台進程組:
斷開與終端的關系(並非輸入、輸出、錯誤輸出關系),進程組關系,不再受制於終端而存在,這種進程與進程組被稱之為后台的
后台進程存在的理由:需要常駐內存,提供一個服務
創建進程
(1)、創建進程:execv()和fork()
execv():不能使得進程數目增加,只是將外存以進程方式加載一個可執行文件到內存;這就相當於”替換“之術
fork():分身之術
pid = fork()
此時pid > 0,是父進程執行的代碼段,但是此時的pid是子進程的PID
pid == 0,是子進程執行的代碼段,表示創建子進程成功
pid < 0 創建進程失敗,子進程返回-1,父進程返回錯誤編號
fork()得到的子進程和父進程的數據段、代碼段、棧等數據內容是一樣的
但是進程是高度獨立的,所以其數據並沒有共享;
每個進程所能進行的內存操作都是站在虛擬內存基礎上的,一個進程在沒有特殊手段的前提下,它只能看到自身,而看不到其他進程的存在!!!
因為虛擬內存的存在,進程以為自己所能訪問的內存大小是:整個計算機的內存
進程的消亡:exit()
進程的消亡過程:進程通過調用exit告知操作系統內核其要結束。這是因為PCB的管理權是操作系統內核的,操作系統內核需要知道進程的結束,以便:(1)、告知該進程的父進程"你的子進程要結束了" (2)、回收PCB資源
(2)、fork()的優化
子進程並不需要父進程的所有數據,從父進程復制的數據都是無效的
優化:
i、當調用fork()產生子進程時,主要是將PCB先進行復制
ii、得到PCB之后,並為其分配了物理地址映射之后,並不會立即將父進程的內容復制給子進程
iii、而是進行了指向引用技術(子進程的代碼段內容直接指向父進程的代碼段),子進程的數據段內容一開始也是指向引用,堆段也是如此
iiii、Copy On Write技術:如果子進程或父進程要修改數據段/堆段的內容,則立即將要修改的部分內存拷貝出一份,然后才可以修改;從而在邏輯上保證了進程的邏輯性和封閉性
經過如上4個步驟的調整,fork()之后不會消耗過大的CPU和內存資源
技術的實現關鍵:進程的虛擬地址與計算機的物理地址映射關系的管理
此時就不會因為fork()而產生巨大的浪費
轉自:
https://mp.weixin.qq.com/s/37fvXePIIxZFnklV_t0g5Q
《unix高級環境編程》