第3章 進程管理

一、進程

進程就是處于執(zhí)行期的程序以及相關(guān)資源的總稱(目標碼存放在某種存儲介質(zhì)上),是正在執(zhí)行的程序代碼的實時結(jié)果。內(nèi)核需要有有效而又透明地管理所有細節(jié)。

執(zhí)行線程,簡稱線程,是在進程中活動的對象。每個線程都有一個獨立的程序計數(shù)器、進程棧和一組進程寄存器。內(nèi)核調(diào)度的對象是線程,而不是進程。

二、進程描述符及任務(wù)結(jié)構(gòu)

內(nèi)核把進程的列表存放在任務(wù)隊列(task_list)雙向循環(huán)鏈表中。鏈表的每個元素都是類型為task_struct的結(jié)構(gòu),稱為進程描述符(process descriptor)。進程描述符中包含一個具體進程的所有信息,該結(jié)構(gòu)在32位機器上大小約1.7KB:


image.png

內(nèi)核通過一個唯一的進程標識值或PID來標識每個進程。PID是一個數(shù),表示為pid_t隱含類型,實際是一個int類型。其最大值默認為32768,就是系統(tǒng)允許同時存在的進程最大數(shù)目。

進程的狀態(tài)由task_struct中的state域描述,系統(tǒng)中每個進程必然處于以下5中進程狀態(tài):TASK_RUNNING、TASK_INTERRUPTIBLE、TASK_UNINTERRUPTIBLE、__TASK_TRACED、__TASK_STOPPED:

image.png

Linux中進程之間存在明顯的集成關(guān)系。所有的進程都是PID為1的init進程的后代。內(nèi)核在系統(tǒng)啟動的最后階段啟動init進程,該進程讀取系統(tǒng)的初始化腳本并執(zhí)行其他的相關(guān)程序,最終完成系統(tǒng)啟動的整個過程。
task_struct中包含:

  • task_struct* parent:指向父進程
  • list_head children:子進程鏈表
  • list_head sibling:兄弟進程鏈表

三、進程創(chuàng)建

Unix使用fork()和exec()來創(chuàng)建進程空間、讀入可執(zhí)行文件并執(zhí)行。

Linux的fork(調(diào)用clone實現(xiàn))使用寫時拷貝(copy-in-write)頁實現(xiàn)。寫時拷貝是一種可以推遲甚至免除拷貝數(shù)據(jù)的技術(shù)。內(nèi)核此時并不復(fù)制整個進程地址空間,而是讓父子進程共享同一個拷貝,只有在需要寫入時才進行拷貝。因此fork的實際開銷就是復(fù)制父進程的頁表以及給子進程創(chuàng)建唯一的進程描述符。

四、線程在Linux中的實現(xiàn)

線程機制是現(xiàn)代編程技術(shù)中常用的一種抽象概念。該機制提供了統(tǒng)一程序內(nèi)共享內(nèi)存地址空間允許的一組線程。

Linux把所有線程當作進程來實現(xiàn),內(nèi)核沒有準備特別的調(diào)度算法或是定義特別的數(shù)據(jù)結(jié)構(gòu)來表征線程。線程僅僅被視為一個與其他進程共享某些資源的進程。每個線程都有唯一隸屬于自己的task_struct。對于Linux而言,線程只是一種進程間共享資源的手段。

線程的創(chuàng)建

線程的創(chuàng)建和普通進程類似。只不過在clone時需要傳一些參數(shù)標志來指明需要共享的資源:

clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0);

內(nèi)核線程

內(nèi)核經(jīng)常需要在后臺執(zhí)行一些操作。這種任務(wù)可以通過內(nèi)核線程(kernel thread)完成--獨立允許在內(nèi)核空間的標準進程。內(nèi)核進程沒有獨立的地址空間,不會切換到用戶空間,只能由其他內(nèi)核線程創(chuàng)建。

五、進程終結(jié)

一般來說,進程的析構(gòu)都是自身引起的,發(fā)生在進程調(diào)用exit時,可能是顯示調(diào)用,也可能是隱式從某個程序的主函數(shù)返回調(diào)用。當進程接受到它不能處理也不能忽略的信號或異常時,它還可能被動地終結(jié)。

exit會將進程相關(guān)聯(lián)的資源都釋放了,此時進程處于EXIT_ZOMBIE退出狀態(tài),占用的內(nèi)存包括:內(nèi)核棧、thread_info結(jié)構(gòu)和task_struct結(jié)構(gòu)。此時進程的目的就是向其父進程提供信息。

父進程通過調(diào)用wait掛起,知道其中一個子進程退出。此時,才會將子進程的內(nèi)核棧、thread_info結(jié)構(gòu)和task_struct結(jié)構(gòu)所占內(nèi)存釋放。

如果父進程在子進程之前退出,那么會給子進程在當前線程組內(nèi)找一個線程作為父進程,如果不行,就讓init進程作為其父進程。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容