linux核心--進程管理

來源:互聯網
上載者:User

標籤:處理器   linux   程式   空間   

      進程就是處理執行期的程式(目標代碼存放在某種儲存介質上)。查進程並不僅僅局限於一段可執行程式代碼。通常進程包括:

  • 開啟的檔案
  • 掛起的訊號
  • 核心內部資料
  • 處理器狀態
  • 地址空間
  • 一個或多個執行線程
  • 存放全域變數的資料區段

      對linux而言,線程是特殊的進程,並不特別區分。在現代作業系統中,進程提供兩種虛擬機器制:虛擬處理器和虛擬記憶體。雖然實際上可能是許多進程正在分享同一個處理器,但虛擬處理器給進程一種假象,讓這些進程覺得自己在獨享處理器。而虛擬記憶體讓進程在擷取和使用記憶體時覺得自己擁有整個系統的所有記憶體資源。同一個進程中的多個線程可以共用虛擬記憶體,但擁有各自的虛擬處理器。

進程描述符及任務結構

      核心把進程存放在叫做任務隊列(task list)的雙向迴圈鏈表中。鏈表中的每一項都是類型為task_struct、稱為進程描述符(process descriptor)的結構,進程描述符中包含一個具體進程的所有資訊。進程描述符中包含的資料能完整的描述一個正在執行的程式:它開啟的檔案、進程的地址空間、掛起的訊號、進程的狀態等。

      進程描述符結構圖:

      650) this.width=650;" title="進程描述符" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="進程描述符" src="http://img1.51cto.com/attachment/201409/7/817917_1410058281OJzq.jpg" "394" height="342" />

      核心通過一個惟一的進程標識值(process identification value)或PID來標識每一個進程。核心把每個進程的PID存放在它們各自的進程描述符中。linux系統預設的PID最大值為32768,也可以通過修改核心參數kernel.pid_max = 65535來提高上限。

      進程描述符中的state域描述了進程的目前狀態,系統中的每個進程必然處理5種進程狀態中的一種:

  • task_running(運行):進程是可執行檔,它或者正在執行,或者在運行隊列中等待執行。這是進程在使用者空間中執行惟一可能的狀態;也可以應用到核心空間中正在執行的進程
  • task_interruptible(可中斷):進程正在睡眠(也就是說它被阻塞),等待某些條件的達成。一旦這些條件達成,核心應該會把進程狀態設定為運行。處於些狀態的進程也會因為接收到訊號而提前被響醒並投入運行。
  • task_uninterruptible(不可中斷):除了不會因為接收到訊號而被響醒從而投入運行外,這個狀態與可打斷狀態相同。這個狀態通常在進程必須等待時不受幹擾或等待事件很快就會發生時出現。
  • task_zombie(僵死):該進程已經結束了,但是其父進程還沒有調用wait4()系統調用,為了父進程能夠獲知它的訊息,子進程的進程描述符仍然被保留著。一旦父進程調用了wait4(),進程描述符就會被釋放。
  • task_stopped(停止):進程停止執行;進程沒有投入運行也不能投入運行。通常這種狀態發生在接收到SIGSTOP、SIGTSTP、SIGTIN、SIGTTOU等訊號的時候。此外,在調試期間接收到任何訊號,都會使進程進入這種狀態。

       進程的狀態轉換圖:

650) this.width=650;" title="進程的狀態轉換" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="進程的狀態轉換" src="http://img1.51cto.com/attachment/201409/7/817917_1410058282Wqh6.jpg" "642" height="525" />

      linux系統進程之間存在一個顯顯的繼承關係,所有進程都是PID為1的init進程的後代,核心在系統啟動的最後階段啟動init進程,該 進程讀取系統的初始化指令碼並執行其它的相關程式,最終完成系統啟動的整個過程。系統中的每個進程必有一個父進程,相應的,每個進程也可以擁有零個或多個子進程。進程間的關係存放在進程描述符中,每個task_struct都包含一個指向其父進程task_struct、叫做parent的指標,還包含 一個稱為children的子進程鏈表。

 

進程建立

      linux進程建立使用兩個函數fork()和exec()來完成。首先,fork()通過拷貝當前進程建立一個子進程。子進程與父進程的區別僅僅在於PID(每個進程唯一)、PPID(父進程的進程號)和某些資源和統計量。exec()函數負責讀取可執行檔並將其載入地址空間開始運行。

      傳統的fork()系統調用直接把所有的資源複製給新建立的進程,實種實現過於簡單並且效率低下,因為它拷貝的資料也許並不共用,而且,如果新進程打算立即執行一個新的映像,那麼所有的拷貝都將前功盡棄。linux fork()使用寫時拷貝(copy-on-write)而實現。寫時拷貝是一種可以延遲甚至免除拷貝資料的技術。核心此時並不複製整個地址空間,而是讓父進程和子進程同時共用同一個拷貝。只有在需要寫入的時候,資料才會被複製,從而使各個進程擁有各自的拷貝。也就是說,資源的複製只有在需要寫入的時候才進行,在些這前,只是以唯讀方式共用。這種技術使地址空間上的頁的拷貝被延遲到實際發生寫入的時候。在頁根本不會被寫入的情況下,舉例來說:fork()後立即調用exec(),fork()的實際開銷就是複製父進程的頁表以及給子進程建立惟一的進程描述符。在一般情況下,進程建立後都會馬上運行一個可執行檔檔案,這種最佳化可以避免拷貝大量根本就不會使用的資料。

線程在linux中的實現

      線程機制提供了同一程式內共用共用記憶體位址空間啟動並執行一組線程。這些線程還可以共用開啟的檔案和其它資源。線程機制支援並發程式設計技術,在多處理器系統上,它也能保證真正的平行處理。linux把所有線程都當作進程來實現,線程僅僅被視為一個與其它進程共用某些資源的進程。每個線程都擁有惟一隸屬於自己的task_struct,所以在核心中,它看起來就像一個普通的進程。

       對於linux來說,線程只是一種進程間共用資源的手段,假如有一個包含4個線程的進程,通常會有一個包含指向4個不同線程進程的指標的進程描述符。該描述符負責描述像地址空間、開啟的檔案這樣的共用資源。

進程終結

      當一個進程終結時,核心必須釋放它所佔有的資源,並把這一不幸告知其父進程。當父進程在子進程之前退出時,必須有機制來保證子進程找到一個新的父親,否則這些成為孤兒的進程就會在退出時永遠處理僵死狀態,白白的消耗記憶體。對於這個問題,解決的方法是給子進程在當前線程組內找一個線程作為父行,如果不行,就讓init進程來作為它們的父親。

linux核心--進程管理

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.