【操作系統原理】第二章-進程和線程

進程和線程

進程

什麼是進程

  在操作系統中,操作系統需要對各種資源進行管理,大概可以分爲以下幾類:內存,文件,磁盤,進程。所謂進程就是操作系統有序管理應用程序的執行的方式,來保證以下幾點:
  1、所有資源對多個應用程序是可用的。
  2、物理處理器在多個應用程序中切換,保證所有程序都在執行中。
  3、處理器和I/O設備都能得到充分的利用。
  因此所有現代操作系統都依賴於一個模型,在該模型中,一個應用程序對應一個或多個進程。進程的定義有以下幾條:
  1、一個正在執行的程序。
  2、一個正在計算機上執行的程序實例。
  3、能分配給處理器並由處理器執行的實體。
  4、由一組執行的指令、一個當前狀態和一組相關的系統資源表徵的活動單元。
  簡單來說什麼是進程,進程就是正在執行中的程序。而在操作系統中,操作系統爲了更好的描述一個進程,於是將進程視爲一些元素組成的實體,而其中最重要的兩個元素是程序代碼數據集。一般來說一個程序有了程序代碼和數據集就可以順利執行了,但是操作系統說還不夠,爲了滿足操作系統對進程的控制,例如調度,中斷,執行等操作,操作系統將每個進程描述爲一個叫做進程控制塊(PCB) 的數據結構,在PCB中存儲着操作系統對控制一個進程所需要的全部信息,可以根據PCB找到程序代碼,找到程序的數據,程序獲得的資源等等。所以一個進程對於操作系統來說就是一個PCB。PCB中所存儲的信息我們在下文中有詳細介紹。
  知道了操作系統是通過PCB管理進程的後接下來討論進程的狀態。

進程狀態

兩狀態模型

  在多道操作系統中,我們假設現在的處理器都是單核的即同時只能有一個進程正在處理器中執行,但是操作系統爲了讓用戶看上去所有進程都在“同時”運行於是他在操作系統中設置了時間片,即一個進程可以連續執行的最大時間,並且按照調度算法快速在不同進程間進行切換執行,執行中的進程狀態爲運行態,而未執行的則成爲非運行態,其中關係如下圖。
進程狀態
  同時我們可以把非運行態的進程組織到隊列中,每次切換進程從隊列中調出一個進程開始運行,而切換下來的進程要麼重新加入隊列要麼執行完畢退出,如下圖。
進程狀態
  這裏提一下可能導致創建新進程的事件和可能導致進程退出的事件。
  進程創建由以下4種事件觸發:
  1、新的批處理作業。新的批處理作業進入操作系統肯定會創建新的進程來執行批處理作業。
  2、用戶登錄。用戶登錄往往也會創建新進程來執行用戶指令,之所以使用進程是爲了將用戶與操作系統隔離,一個用戶指令的崩潰不會影響到其他用戶乃至操作系統。
  3、爲提供服務由操作系統創建。有時操作系統爲了提供一個服務也會創建新的進程,例如用戶進程請求打印一個文件,系統可以創建一個管理打印的進程,進而使請求進程可以繼續執行。
  4、由現有進程派生。當現有進程引發另一個進程的創建時,操作系統也會創建新的進程,這就是進程派生,這往往很有用,派生出的進程可以幫助主進程處理數據,組織數據等等。
  進程的終止由以下14種事件觸發:
  1、正常完成。正常結束運行。
  2、超過時限。進程運行超過規定的時限。
  3、無內存可用。系統無法滿足進程需要的內存。
  4、超出範圍。進程試圖訪問非法的內存單元。
  5、保護錯誤。進程試圖使用不允許使用的資源或文件。
  6、算術錯誤。進程試圖進行被禁止的運算。
  7、時間超出。進程等待某一事件發生的時間超過了規定的時間。
  8、I/O失敗。在輸入輸出期間發生錯誤。
  9、無效指令。進程試圖執行一個不存在的指令。
  10、特權指令。進程試圖使用爲操作系統保留的指令。
  11、數據誤用。錯誤類型或未初始化的一塊數據。
  12、操作員或操作系統干涉。操作員或操作系統終止進程。
  13、父進程終止。在某些操作系統中,父進程終止時操作系統會自動終止該進程的所有子進程。
  14、父進程請求。父進程要求終止其子進程。

五狀態模型

  如果所有進程都做好了準備,操作系統會從未運行隊列中以輪轉的方式調度每個進程。但是這裏有個問題,如果並非所有進程都做好了準備呢?也許未運行的進程中有些進程正在等待某一事件的發生,也就是處於阻塞,因此單純的對所有未運行的進程進行輪轉是不科學的,應該對所有已經就緒的進程進行調度。解決這種問題的最好方法就是將未執行進程隊列拆分爲兩個隊列分別是就緒隊列和阻塞隊列,由此進程的狀態由2狀態變爲了3狀態,此外還要增加新建退出態,這十分有必要。改進後的狀態模型如下圖所示。
進程狀態
  運行態:進程正在執行。
  就緒態:進程做好了準備,隨時接收調度。
  阻塞態:進程在等待某些事件的發生,在事件發生前不能執行,如I/O操作。
  新建態:剛剛新建的進程,操作系統還未將其加載至內存,通常是PCB已經創建但是還並未加載到內存中的新程序。
  退出態:操作系統從可執行進程組中釋放的進程。
  新建態與推出態十分有必要。在一個進程被新建時它並非絕對會被調入內存,通常是分兩步,首先創建該進程的PCB,並與之關聯,但是此時可能面臨內存不足或者操作系統限制了最大進程數導致這個進程還無法被調入進程,因此該進程被暫時留在新建態,在這個狀態的進程PCB已經創建並且加載進內存,但是進程的代碼和數據往往還留在外村中等待加載。
  退出態也和新建態同理。當進程因爲某些人原因要被終止時,此時並不直接將其調出內存,首先操作系統會停止執行該進程的代碼,但是暫時讓其留在內存中,因爲某些輔助程序或是支持程序會來記錄該進程相關數據和信息,此時進程停留在退出態。等相關程序收集完所需信息後,再將其所有數據從內存中移除。
  關於阻塞,就緒和運行三種狀態的轉換更爲普遍和便於理解。操作系統從就緒隊列中調度某個進程進入運行態運行,當時間片結束後操作系統將其放回就緒態執行其他進程,如果在執行期間進程必須等待某些事件,便將其放入阻塞態,然後調度其他進程執行。當該進程等待的事件完成後操作系統則將其放回就緒態等待調度。
  但是此時又有一個問題,如果所有阻塞進程放在同一個阻塞隊列中,當一個事件完成後操作系統不得不掃描整個隊列找到那些等待該事件的進程然後將其放進就緒隊列中,這樣的效率十分低下,因此通常是爲每一個事件創建一個阻塞隊列。同理當按照優先級進行調度時,也會將優先級相同的進程放進一個就緒隊列,避免掃描等低效的做法,這是典型的用空間換時間的做法。
進程狀態

七狀態模型

  在介紹七狀態模型前,我們思考一個問題,三個基本狀態(就緒,運行和阻塞)的所有進程都必須存儲在內存中,此時就可能出現一種情況,即所有進程都處於阻塞態,沒有就緒狀態的進程,此時又開始了處理器的空轉,處理器沒辦法執行進程只能開始等待進程從阻塞態恢復就緒態,並且加入此時又有新的進程處於新建態,由於內存不足,處於新建態的進程也沒辦法進入內存無法執行,這是一個十分致命的處理器空轉問題,解決這個問題有兩個方法:擴大內存,很顯然成本太高了;將阻塞態的進程暫時調出內存放回磁盤,來讓新建態的進程有足夠內存進入就緒態開始處理器的調度和運行。
  但是在將一個阻塞態進程掛起後,操作系統可以選擇接納一個新建態進程進入就緒隊列,也可以選擇將一個之前掛起的進程恢復就緒態,並且爲了減少操作系統的負載操作系統更傾向於後者。但是處於掛起的進程也可能還並未接觸阻塞,將一個阻塞進程放回內存沒有任何意義,於是更好的方法是將掛起區分爲兩個狀態即就緒/掛起態阻塞/掛起態,這樣每次操作系統就只需要考慮是否應該把進程從就緒/掛起態換回就緒態即可。完整的七狀態模型如下:
進程狀態
  阻塞/掛起態:進程在外存中並等待一個事件。
  就緒/掛起態:進程在外存中,但只要載入內存即可開始運行。
  並且操作系統允許進程從就緒變爲就緒/掛起態,或從阻塞/掛起態變更爲阻塞態,只是這樣做的意義不大,因此並不會這樣做。
  導致進程被掛起的事件有以下幾種:
  1、交換。爲了釋放內存空間。
  2、其他OS原因。操作系統可能會掛起後臺進程或者工具進程,或掛起可能會導致問題的進程。
  3、交互式用戶請求。用戶希望掛起一個進程來進行調試。
  4、定時。進程可被週期性的執行,並在等待下一個時間間隔時掛起。
  5、父進程請求。父進程可能希望掛起後代進程的執行,以檢查或修改掛起的進程。

進程描述

進程在操作系統中的描述方式

  操作系統可以管理計算機內的任何資源,包括內存、設備、文件和進程但是操作系統是如何管理的呢?對於操作系統來說,所有的資源都被組織成對應的數據結構,內存對應內存表,設備對應設備表,文件對應文件表,進程自然也有進程表,如下圖。接下來我們將詳細介紹操作系統如何描述操作系統中的所有進程,也就是進程表的結構。
進程描述
  如上圖所示,進程表中存放着一個一個進程,而每個進程項都指向一個進程映像,什麼是進程映像呢?我們說一個進程最基本的元素是用戶代碼以及元素集,初次之外還有若干操作系統控制進程所需的信息,這些信息都存放在進程映像中,並且還有一個進程用於存儲臨時數據的棧,因此進程映像中的典型元素可以概括如下:
  1、用戶數據。用戶空間中的可修改部分,包括程序數據、用戶棧區域和可修改的程序。
  2、用戶程序。待執行的程序。
  3、棧。每個進程有一個或多個後進先出棧,棧用於保存參數、過程調用地址和系統調用地址。
  4、進程描述塊。操作系統控制進程所需的數據。
  有了以上信息就有了一個進程調度,運行所需的全部數據,這些數據在內存中有可能是連續的也有可能是不連續的,這根據操作系統內存管理的方式來決定,但是但從操作系統描述管理進程方式來看,操作系統通過在內存中的主進程表,每一表項都至少包含一個指向進程映像的指針,通過進程表操作系統可以找到控制進程所需的全部數據。

進程屬性

  我們知道了操作系統通過進程表和進程映像描述進程,進程映像中的用戶數據和用戶程序都是根據用戶所寫的程序而定的,棧也只是用來保存參數調用地址所用的臨時儲存空間,但是其中我們要尤爲重要介紹PCB(進程描述塊)。正如之前所說進程描述塊中儲存了操作系統控制進程所需的一切信息,對於操作系統來說拿到進程控制塊就可以控制進程進行調度等操作,那麼進程控制塊中到底存放了進程哪些信息呢?
  不同操作系統的PCB中組織的信息是不同的,但是PCB中所有操作系統都需要的共用基礎信息一共8種:
  1、標識符:PID,與進程相關的唯一標識符。
  2、狀態:進程狀態,狀態的劃分是接下來介紹的重點。
  3、優先級:與進程調度有關的優先級。
  4、程序計數器:程序中即將執行的下一條指令的地址。
  5、上下文數據:進程執行時處理器的寄存器中的數據。
  6、內存指針:包括程序代碼及相關數據的指針,以及與其他進程共享內存的指針。
  7、I/O狀態信息:進程的I/O請求,分配給進程的I/O設備和進程使用文件
  8、記賬信息:包括處理器時間綜合、使用的時鐘數綜合、時間限制、記帳號等。
  這些信息一共可以分爲三類進程標識信息處理器狀態信息進程控制信息。進程標識信息典型的就是標識符,他是一個操作系統中唯一標識一個進程的基本索引。處理器狀態信息由處理器寄存器的內容組成,中斷進程時,必須保存寄存器中的所有信息,以便進程恢復時使用,這些信息就保存在PCB中,典型的有上下文數據。進程控制信息是操作系統控制和協調各種活動進程所需的額外信息,例如進程優先級。
  根據以上的介紹,進程映像在虛存中的結構基本如下圖所示,但是具體情況還得視操作系統的具體管理方案而定。
進程描述

進程控制

執行模式

  操作系統必須保證自己的安全性,因此再讓用戶進程運行時並不能將所有的權限交給用戶,這樣操作系統很可能會被進程搞到崩潰,最好的方式是操作系統將一些特權指令不進行公開,用戶進程不能直接執行這些指令,但是操作系統允許進程發起使用特權指令的請求,然後再有操作系統自己代替用戶執行指令,這樣可以大大增強操作系統的健壯性,同時內存也並不會讓用戶進程都可以訪問到,如果修改了操作系統即內核可能會發生致命錯誤,於是這中間操作系統加入了種種限制,先從一個進程的執行上來說,操作系統將其分爲了兩種模式用戶模式(用戶態/目態)內核模式(內核態/管態)
  用戶進程默認是在用戶模式下運行,在用戶模式下進程的權限受到控制,而如果發生了一些特殊事件,例如請求系統調用模式會從用戶模式轉換爲內核模式。說白了用戶模式即處理器在執行用戶代碼,內核模式即處理器目前在執行內核代碼。那麼這樣有出現兩個問題,處理器如何知道它正在什麼模式下執行?一般情況下,程序轉太子中通常存在一個只是執行模式的位,該位會因模式的變化而變化,也就是說在處理器的一個寄存器中存儲了當前處理器處於什麼模式下的信息。例如Intel Itanium處理器中就有一個包含2位CPL(當前特權級別)字段的處理器狀態寄存器用於存儲模式信息。

進程創建

  操作系統在創建一個進程的時候會進行哪些工作呢?當操作系統決定創建一個進程時會執行以下操作:
  1、爲新進程分配一個唯一的進程描述符。
  2、爲進程分配空間。
  3、初始化PCB。
  4、設置正確的鏈接。例如將進程放到就緒隊列中,而就緒隊列是一個鏈表,此時就需要在數據結構上進行連接。
  5、創建或擴充其他數據結構。例如創建賬單和評估性能。

進程切換和模式切換

進程切換

  進程切換在什麼時候發生呢?理論上在任何時刻只要操作系統拿到控制權就可以進行進程切換,那麼什麼時候操作系統會重新拿到控制權呢?
  這裏首先考慮中斷的情況,而中斷又可分爲兩種:中斷陷阱。中斷一般是與當前正運行進程無關的某種外部事件相關,例如完成了一次I/O操作,中斷處理器完成一些基本的輔助操作後將控制權轉給與已發生的終端相關的操作系統歷程,簡單來說中斷的發生屬於正常的事件,不過是操作系統暫時停止執行當前進程轉爲處理另外一件更加緊急的事情。例如以下三種中斷:
  1、時鐘中斷。當前進程時間片到期,轉爲從就緒隊列中調度新的進程開始運行。
  2、I/O中斷。某一I/O完成,操作系統判斷是否有正在等待該I/O的進程,如果有將其放回就緒態,隨後操作系統根據調度算法調度合適的進程繼續運行。
  3、缺頁中斷。處理器遇到一個引用不存在內存中的虛存地址時,此時會發生缺頁中斷,然後操作系統要根據算法將訪問的頁調入內存,這塊的處理與操作系統對內存管理有很大關係。
  除了中斷,陷阱也有可能會導致進程狀態的切換。所謂陷阱就是異常或者錯誤。即發生在程序內部的不可預期的非法錯誤。如果錯誤致命則將當前進程改爲退出態,不致命時操作系統的行爲決定於操作系統的設計,有可能是簡單的通知用戶,也有可能是嘗試恢復。
  還有一種可能會導致進程切換的事件,就是系統調用。當用戶進程發起一個特權指令(系統調用)時,操作系統會將當前用戶進程設置爲阻塞態,然後會調用系統例程執行系統調用指令,當執行完畢會在此調度用戶進程開始執行。
  綜上所述,可能造成進程狀態切換的事件有三種中斷,陷阱(異常),系統調用

模式切換

  操作系統爲了安全設置了不同的執行模式,那麼操作系統何時進行模式切換呢?我們知道內核模式就是處理器在執行內核中的系統代碼,那麼不難得出,只要發生狀態轉換的事件一定會造成模式轉換。例如中斷,不管時哪一種中斷,都少不了操作系統要根據調度算法重新調度進程開始運行,更不用說缺頁中斷中操作系統還需要進入內核狀態執行內存置換算法換頁等等;異常也是需要操作系統判斷如何進行下一步處理也需要進行模式切換;系統調用就是在執行系統歷程,更需要模式的切換,因此我們可以得出進程模式切換的基本事件就是中斷,陷阱(異常),系統調用
  但是要注意的時,並非模式切換一定會導致運行態進程切換,例如在中斷後操作系統根據調度算法決定繼續執行當前用戶進程,那麼當前用戶進程就完全不需要改變狀態,相比切換運行態進程單單切換模式,操作系統所要做的操作可要少多了。所以進程切換一定會導致模式切換,但進程模式切換並不一定會發生進程狀態切換。

操作系統的組織形式

  我們之前的介紹都基於操作系統是在所有進程獨立外的一個大型程序,是一組進程,那麼操作系統到底是進程麼?如果是進程的話又要怎麼控制它?
  以下是幾種操作系統內核的設計方式。

無進程內核

  這種設計方式在許多老操作系統中都十分常用,是一種相當傳統的設計方式。這種設計方式的原則是將操作系統視爲獨立於每個用戶進程外執行的一個大的系統內核。我們每次要執行操作系統代碼例如發生中斷,陷阱,系統調用時,都需要進行代碼及及數據的切換,將用戶代碼及數據暫時保存然後執行操作系統內核代碼,執行完畢後恢復調度用戶進程或是調度其他進程。在這種設計方法下,進程這一概念僅適用於用戶程序,操作系統代碼則是在內核模式下單獨運行的實體。下圖爲這種設計方法的示意圖:
組成形式

在用戶進程內運行

  較小的計算機操作系統通常採用這種設計方式,這種方式是將系統內核代碼放到每個進程虛存中的共享區,這樣做的好處是如果要執行系統代碼不需要像無進程內核那樣切換代碼及數據以切換系統歷程,這種方式仍然是在每個用戶及進程內部執行操作系統代碼,不需要切換進程,只用在同一進程中切換模式即可,所帶來的系統開銷更小,更加快捷。並且在一個進程內用戶程序和操作系統程序都可執行,而在不同用戶進程中執行的操作系統程序是相同的,這也是爲什麼要將系統內核放到共享地址空間的原因,在這種方式下一個進程在虛存中的映像如下:
組成形式
  這種設計方式的示意圖如下:
組成形式

基於進程的操作系統

  這種設計方式是把操作系統作爲一組系統進程來實現。和其他方法一樣同樣是在內核模式下運行系統代碼,但是在這種情況下是吧內核功能都組織爲獨立的進程,但同時往往也將一些進行進程切換工作的代碼獨立出來。這種方式的好處是使用模塊化系統設計的原理,可以將一些操作系統功能作爲獨立進程來實現,同時這種方式在多處理或多繼環境中很有用。這種設計方式的示意圖如下:
組成形式

線程

線程和進程

  現代的大多數操作系統都支持線程的使用,因爲進程所具有的兩個特點資源所有權調度,但是操作系統更希望將這兩個特點分開進行處理,於是便誕生了線程,我們將進程視爲資源分配的基本單位,將線程視爲處理器調度的基本單位,線程也可以視爲一個輕量級進程

多線程

  多線程是指操作系統允許在單個進程內有多個併發執行路徑,一個併發執行路徑又被成爲一個線程。早期各個版本的操作系統他們支持多用戶進程,即允許一個任務內擁有多個進程進行併發處理,但是每個進程內部只允許有一條執行路徑,也就是隻允許擁有一個線程,而如今的現代操作系統中絕大多數操作系統支持多線程方法,其中的差別可用下圖表示:
多線程
  在多線程的基礎上程序併發將會更容易實現,因爲線程是一個輕量級進程因此切換和調度的消耗會更少,並且同一進程內的線程之間共享虛擬地址空間,因此通訊會更加方便。在多線程環境中,進程定義爲資源分配基本單位和一個保護單位,一個進程內部有:
  1、容納進程映像的地址空間。
  2、對處理器、其他進程(進程間通訊)、文件和I/O資源(設備和通道)的受保護訪問。
  一個進程中可能有一個或多個進程,每個線程都有:
  1、一個線程執行狀態。
  2、未運行時保存的線程上下文。
  3、一個執行棧。
  4、每個線程用於局部變量的一些靜態存儲空間。
  5、與線程內其他線程共享的內存和資源的訪問。
  在多線程環境下,每個進程依然有自己的進程控制塊以及進程映像,但是進程中的每個線程也擁有屬於自己的獨立的棧以及線程控制塊,控制塊中存儲着線程狀態,調度優先級,上下文數據等,這些是每個線程獨立的信息,除此之外進程內的代碼段用戶數據段包括進程控制塊中的信息在進程內各個線程間共享,因此纔可以做到進程內各個線程之間都駐留在同一地址空間中可以做到除獨立信息外的數據及代碼共享。但是每個操作系統對多線程環境的實現方法都不盡相同,具體實現方法視具體環境而定,但是都應該滿足進程和線程各自的基本特點。模式如下:
多線程

進程和線程之間的區別

  這是十分常見的問題,在此做同一歸納和梳理:
  1、進程是資源分配的基本單位,線程是處理器調度的基本單位。
  2、同一進程內線程共享進程狀態和資源,例如數據段,代碼段,I/O信息等。但是每個線程內也有獨立的數據,每個線程都擁有屬於自己的棧,線程屬性信息存在線程控制塊中,例如上下文數據,線程狀態,調度信息等。
  3、線程是輕量級進程,因此創建和銷燬所消耗的系統資源更少,更快。
  4、同一進程內線程切換所消耗的資源相比進程切換更少且更快。
  5、同一進程內線程共享大部分數據因此通信起來更加方便,無需藉助內核。

線程分類

  在討論線程分類前我們先思考這麼幾個問題:如果進程中的一個線程因爲請求資源而被阻塞那麼這整個進程是否應該被阻塞?在多核操作系統中統一進程中的線程是否應該允許並行執行?
  根據以上兩個特點,我們將線程可以分爲內核級線程(KLT)用戶級線程(ULT)。它們二者之間各有優點各有特色,當然這都卻決於操作系統的具體實現方式的基礎上。示意圖如下:
線程分類

用戶級線程

  在純ULT操作系統上,對於操作系統來說線程是不可見的,操作系統依然只負責維護進程的相關控制和管理工作,而進程內線程之間的調度以及管理包括通信全部由應用程序自行完成,內核並意識不到現成的存在。用戶可以使用線程庫來將任何一個程序設計成多線程的,並用線程庫完成多線程的管理和控制。
  在這樣的實現方式下由於操作系統並無法意識到線程的存在所以除非用戶自己主動調度線程,操作系統並無法完成進程內線程間的切換,並且如果此時進程內一個線程發生阻塞,例如進行了一次系統調用,系統會將整個進程置爲阻塞態,包括進程內其他線程也會一起阻塞,這是十分不靈活的設計。
  例如假設進程B中有着線程1線程2兩個線程,它們的狀態如(a)所示,現在有可能發生如下情況:
  1、線程2進行了一次系統調用,由於操作系統無視線程的存在,因此它認爲是進程B進行了系統調用,因此將整個進程B進行了阻塞。在此之後線程2依然處於運行態,但是對於操作系統來說線程2實際上並不處於運行態。直到進程B取消阻塞,線程2繼續恢復運行。此時狀態如(b)所示。
  2、時鐘中斷使整個進程B停止運行態轉爲就緒態,此時調度其他進程執行,此時將進程B置爲就緒態,線程2依然處於運行態,直到下次再此調度進程B恢復線程2的運行。如圖©所示。
  3、線程2運行到需要線程1執行某些動作的一個點,應用程序內部將線程2置於阻塞態,開始運行線程1。如圖(d)所示。
用戶級線程
  使用ULT有以下優點:
  1、所有線程管理數據結構都在進程內的用戶地址空間中,線程之間調度不需要內核的參與,因此就不需要模式的轉換,效率更高。
  2、不同的調度程序可以設置不同的線程調度算法,靈活性高。
  3、ULT可在任何操作系統上運行,不需要對系統內核代碼修改以支持ULT。
  但ULT也有着以下缺點:
  1、一個線程的進行了系統調用導致阻塞會阻塞整個進程,影響其他所有線程。
  2、多線程應用程序無法利用多處理技術,即內核一次只能把一個進程交給處理器,因此一個進程中只有一個線程可以執行,這相當於再一個進程內實現了多道程序設計,並無法使一個進程內的線程並行執行。
  當然以上兩種缺點是有辦法彌補的。如果希望程序並行執行就將程序設計爲多進程而非多線程;系統調用使進程阻塞可以使用套管技術解決,即將一個可能會產生阻塞的系統調用轉換爲一個非阻塞系統調用,當然這樣的處理更加繁瑣一些。

內核級線程

  內核級進程就是將進程的管理全權交給內核來處理,用戶使用內核提供的API來控制線程,Windows就是使用這樣的方式來實現線程的。
  KLT的優點就是ULT的缺點,KLT的缺點就是ULT的優點。其中最大的缺點就是每次線程之間的切換都需要內核模式的切換,消耗更大,但是可以肯定的是哪怕KLT線程的消耗再大也是遠遠小於進程之間切換的,因此爲了方便管理目前常用的操作系統都是基於內核級線程來實現的。

混合方法

  在混合方法中,內核級線程會被映射到一些由內核管理的內核級線程上,內核級線程是小於或等於用戶級線程的。當用戶級線程和內核級線程相等時此時等價於使用了純KLT方式。
  這種混合方法在設計合理時可以完美結合KLT和ULT的優點,並克服它們的缺點。目前Solaris就是使用了這種混合線程的方法。

Linux的進程和線程管理

進程管理

  Linux屬於類UNIX操作系統,實現的原理與UNIX進程的實現方法類似,其實大部分的操作系統都要遵循系統設計的基本原理,但是實現細節上會有所不同。在Linux上進程狀態轉換如下圖:
Linux進程管理
  在Linux系統實現中最大的變化就是將阻塞態變爲了可中斷不可中斷兩個狀態,並且加入了停止態
  1、可中斷:這是一個阻塞態,進程正在等待一個事件的結束。
  2、不可中斷:這是一個阻塞態,與可中斷的區別是,此時進程正在等待一個硬件條件,因此屏蔽任何信號。
  3、停止:進程收到信號要求被其他進程暫停執行,並且只能由另一個進程的主動動作恢復運行。

線程管理

  Linux使用一種十分特殊的線程處理方式,它內核中並沒有獨立的線程控制塊,Linux選擇使用PCB模擬實現線程,因此在Linux中PCB其實就相當於是一個線程。
  那麼Linux又是如何實現進程間數據獨立線程間數據共享的呢?Linux將用戶級線程映射到內核級進程上,組成一個用戶級進程的用戶級線程則映射到共享一個組ID的多個Linux內核級進程上,使得同一個組內部的進程共享文件和內存等資源,就像一個進程內部的線程共享資源一樣。也就是說用進程模擬實現線程,通過給進程分組的方式來實現數據的共享和獨立。
  同時Linux在內部又通過命名空間來管理進程的數據。命名空間可使一個進程擁有與其他不同命名空間的進程不同的系統視圖,因此可以獲得不同資源。典型的命名空間有:
  1、Mount命名空間。爲進程提供文件系統層次的特定試圖。
  2、UTS命名空間。與系統配置有關。
  3、IPC命名空間。隔離進程間IPC資源,如信號量。
  4、PID命名空間。隔離進程ID空間。
  5、網絡命名空間。隔離與網絡相關的系統資源。
  6、用戶命名空間。提供容器使其與父進程隔離。

發佈了85 篇原創文章 · 獲贊 17 · 訪問量 3567
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章