Linux下的進程環境


僵屍進程、孤兒進程、守護進程、進程組、會話、前台進程組、后台進程組

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高級環境編程》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM