1. 程式人生 > >Windows執行緒管理和排程機制概述

Windows執行緒管理和排程機制概述

1. 執行緒管理

(1)執行緒,有時被稱為輕量級程序,是程式執行流的最小單元。一個標準的執行緒由執行緒ID,當前指令指標,暫存器集合和堆疊組成。另外,執行緒是程序中的一個實體,是被系統獨立排程和分派的基本單位,執行緒自己不擁有系統資源,只擁有一點在執行中必不可少的資源,但它可與同屬一個程序的其它執行緒共享程序所擁有的全部資源。

(2)程序(Process)定義了一個執行環境,包括它自己私有的地址空間、一個控制代碼表,以及一個安全環境;執行緒(thread)則是一個控制流,有自己的呼叫棧(call stack),記錄了執行的歷史。每個程序都包含一個或多個執行緒,當程序被初始化建立時系統為該程序建立第一個執行緒;當最後一個執行緒結束時,程序也隨之結束。

說道這裡就需要說到執行緒的工作模式

程序中的執行緒既可以在使用者模式下執行,也可以在核心模式下執行。如果一個程序執行在使用者模式下,那麼它就只能訪問程序地址空間;如果執行在核心模式下,那麼它將可以訪問這個地址空間。

使用者級執行緒
應用程式在作業系統提供的單個控制流的基礎上,通過在有些控制點(比如系統呼叫)上分離出一些虛擬的控制流,從而模擬多個控制流的行為。由於應用程式對指令流的控制能力相對較弱,所以,使用者級執行緒之間的切換往往受執行緒本身行為以及執行緒控制點選擇的影響,執行緒是否能夠公平地獲得處理器時間取決於這些執行緒的程式碼特徵。而且,支援使用者級執行緒的應用程式程式碼很難做到跨平臺移植,以及對於多執行緒模型的透明。使用者級執行緒模型的優勢


(1)執行緒切換效率高,因為它不涉及系統核心模式和使用者模式之間的切換;
(2)另外一個好處是應用程式可以採用適合自己特點的執行緒選擇演算法,可以根據應用程式的邏輯來定義執行緒的優先順序,當執行緒數量很大時。但是,相對的會增加應用程式程式碼的複雜度。

核心級執行緒
指作業系統提供的執行緒語義,由於作業系統對指令流有完全的控制能力,甚至可以通過硬體終端來強迫一個程序或是執行緒暫停執行,以便把處理器時間移交給其他的程序或是執行緒,所以,核心級執行緒有可能應用各種演算法來分配處理器時間。執行緒可以有優先順序,高優先順序的執行緒被優化執行,他們可以搶佔正在執行的低優先順序執行緒。在支援執行緒語義的作業系統中,處理器的時間通常是按照執行緒而非進城來分配的,因此,系統有必要維護一個全域性的執行緒表,線上程表中記錄每個執行緒的暫存器、狀態以及其他的一些資訊。然後,系統在適當的時候掛起一個正在執行的執行緒,選擇一個新的執行緒在當前處理器上繼續執行。
核心級執行緒的好處


應用程式無需考慮是夠要在適當的時候把控制權交給其他的執行緒,不比擔心自己霸佔處理器而導致其他執行緒得不到處理器時間。因而應用程式只要按照正常的指令來實現自己的邏輯就可以了,核心會妥善處理好執行緒之間的共享處理器的資源分配問題。但是這樣帶來的代價便是需要在使用者模式和核心模式下進行切換:從使用者模式切換到核心模式,再從核心模式切換到使用者模式。在Intel的處理器上可能需要幾百上千和處理器指令週期。

對於執行緒管理和排程,從籠統來說是這樣的:在Windows的核心結構中,程序和執行緒的核心機制是在微核心中實現的,而管理機制是在執行體中實現的。這是基於機制與策略分工的原則。例如,執行緒排程是由微核心來完成,而執行緒和程序的建立、各種管理屬性的設定則是由執行體來完成的。

既然執行緒作為程式執行流的最小單元,那麼在Windows中,對於執行緒的管理和排程執行緒是這樣的:在Windows NT以後,Windows實現了一個基於優先順序的搶先式多處理器排程系統。排程系統總是執行優先順序最高的就緒執行緒。通常執行緒可在任何可用處理器上執行,但也可限制某執行緒只能在某處理器上執行。

先看一下優先順序是怎麼劃分的。執行緒總共分為32個優先順序,具體看下圖:


Windows實現了基於優先順序的搶佔式(Preemptive)執行緒排程演算法,每個執行緒都有一個基本優先順序(base priority)和一個動態優先順序(priority)。優先順序的值處於0~31之間,共分為三個類別
(1)0級表示系統優先順序,為最低優先順序,僅用於零頁面執行緒
(2)1~15為動態優先順序,在某些情況下執行緒的動態優先順序可以在此範圍內進行微調,例如當一個前臺執行緒從等待狀態被喚醒時,期優先順序將有一點點提升,從而可以儘快獲得處理器的執行權(至少比相同基本優先順序的其他執行緒優先)
(3)16~31為實時優先順序,用於處理一些實時事務

執行緒的基本優先順序=[程序的基本優先順序 - 2,程序的基本優先順序 + 2],由應用程式控制。執行緒的動態優先順序 = [ 程序的基本優先順序 - 2, 31],由NT 核心控制。為了提高排程速度,Windows NT維護了一個稱為就緒點陣圖的32位量。就緒點陣圖中的每一位指示一個排程優先順序的就緒佇列中是否有執行緒等待執行。還有一個稱為空閒點陣圖的32位量用來指示一個處理機是否處於空閒狀態。

2. 執行緒排程

先來看看三個典型的執行緒排程演算法
(1)先到服務演算法。在非搶佔式系統中,這一演算法比較自然,簡單來講,用一個FIFO佇列就可以滿足。所有的執行緒構成一個佇列,最先進入對嘞的執行緒獲得處理器的執行權,得到放棄處理器的執行權時,又回到佇列的隊尾,下一個執行緒繼續執行。若是在這個過程中有新的執行緒加入,將其新增到隊尾。
(2)時間片輪轉排程演算法。處理器的時間被分為最大長度不超過牟哥值的時間片段,成為時間片,然後用輪轉方法分配給每個執行緒。當一個執行緒獲得了處理器的執行權之後,按照自身的邏輯執行下去,知道時間片用完,或者自己主動放棄執行權(比如要等待一個訊號量)。系統在獲得處理器的控制權以後,用輪轉的方式找到下一個正在等待執行的執行緒,讓它繼續執行。
(3)優先順序排程演算法。在時間片輪轉演算法中,一個基本的假設是所有的執行緒都同等重要,這一假設在專用計算機上可能是非常合理的,但是,在現代多用途計算機上,可能難以勝任多種不同型別的應用程式併發執行的實際情形。優先順序排程演算法是這種演算法的一個改進,基本思路是,每個執行緒都有一個優先順序值,高優先順序的執行緒總是優先被考慮在處理器上執行。作業系統在管理執行緒時,可以使用一個優先順序佇列,或者每個優先順序用一個佇列存放所有滿足執行條件的執行緒。

對於上面提到的優先順序機制雖然可以實現排程,但光有執行緒的優先順序的話那麼相同優先順序的執行緒中只有一個執行到底,其他的都處於等待,所以還有一個時間配額的制度。時間配額是一個執行緒從進入執行狀態到系統檢查是否有其他優先順序相同的執行緒需要開始執行之間的時間總和。一個執行緒用完了自己的時間配額時,如果沒有其它相同優先順序執行緒,Windows 2000將重新給該執行緒分配一個新的時間配額,並繼續執行。每個執行緒都有一個代表本次執行最大時間長度的時間配額。

在每個執行緒擁有優先順序和時間配額後,通常執行緒先在許多等待函式的呼叫中進入等待狀態,進入等待狀態執行緒的時間配額不會被重置,而是在等待事件出現時,執行緒的時間配額被減1相當於1/3個時鐘間隔;如果執行緒的優先順序大於等於14在等待事件出現時,執行緒的優先順序被重置。可能在這兩種情況下出現搶先的情況:一個是高優先順序執行緒的等待完成,即一個執行緒等待的事件出現。 一個是一個執行緒的優先順序被增加或減少。 在判斷一個執行緒是否被搶先時,並不考慮執行緒處於使用者態還是核心態,排程器只是依據執行緒優先順序進行判斷。 當執行緒被搶先時,它被放回相應優先順序的就緒佇列的隊首。 如果剛用完時間配額的執行緒優先順序降低了,系統將尋找一個優先順序高於剛用完時間配額執行緒的新設定值的就緒執行緒。

如果剛用完時間配額的執行緒的優先順序沒有降低,並且有其他優先順序相同的就緒執行緒,系統將選擇相同優先順序的就緒佇列中的下一個執行緒進入執行狀態,剛用完時間配額的執行緒被排到就緒佇列的隊尾(即分配一個新的時間配額並把執行緒狀態從執行狀態改為就緒狀態)。 如果沒有優先順序相同的就緒執行緒可執行,剛用完時間配額的執行緒將得到一個新的時間配額並繼續執行。 當執行緒完成執行時,它的狀態從執行狀態轉到終止狀態。執行緒完成執行的原因可能是通過呼叫函式而從主函式中返回或通過被其他執行緒通過呼叫函式來終止。如果處於終止狀態的執行緒物件上沒有未關閉的控制代碼,則該執行緒將被從程序的執行緒列表中刪除,相關資料結構將被釋放。

對於執行緒的執行狀態每個執行緒從初始化開始到最後終止,其狀態會隨著系統的狀態以及它自身的程式碼邏輯而發生變化。Windows排程演算法的執行緒狀態轉移圖比傳統的搶佔式排程演算法略微細緻一些。本質上每個執行緒都處於兩種狀態之一:
(1)滿足繼續執行的條件,整在排隊或者已經在執行。在這一情況中,執行緒按照優先順序排隊執行;對於多處理器系統,排隊的過程要複雜些,排程器步進要處理多個佇列,而且要考慮每個處理器的就緒執行緒佇列的平衡程度。由於各個執行緒可能存在處理器親和性,所以,此排隊和分發過程要略微複雜一些
(2)不滿足繼續執行的條件,處於等待狀態,或者它的呼叫棧甚至所處的程序被換出記憶體。在這一情況,對於不滿足執行條件的情形,長期處於等待狀態的執行緒,其呼叫棧(設定整個程序)可能被換出記憶體,在這種情況下,一旦執行條件已滿足,則要首先被換回記憶體,然後才能參與排隊分發

歸結我們得到大概的Windows執行緒基本排程機制是:執行緒優先順序制度+時間配額制度,來管理實現的

3. Windows NT中的多執行緒的機制

由於執行緒比程序開銷小而且建立得更快,同一程序內的多個執行緒共享同一塊記憶體,便於執行緒之間資料共享和傳送以及所有的程序資源對執行緒都有效的原因,系統採用多執行緒而不採用多程序來實現多工。
首先一個處理器在一個時刻只能執行一個執行緒,Windows NT允許使用者同時執行多個任務。這是通過下述方法實現的:

(1)執行一個執行緒,直到它被中斷或進入等待狀態;
(2)儲存該執行緒的描述表;
(3)作業系統從等待執行的執行緒佇列中挑選一個動態優先順序最高的執行緒作為執行執行緒,裝入它的描述表(一旦執行緒獲得CPU時間片,它的動態優先順序就降低,但不會低於它的基礎優先順序);
(4)若還存在等待被執行的執行緒,則重複上述過程。
處理器就是這樣根據優先順序不斷地切換執行執行緒,它的高速度使人產生多個執行緒同時執行的錯覺。所以一個處理器實際上仍然只能處理一個執行緒,這只是利用了高速度上的近似同時性。當計算機系統有多個處理器時,多執行緒就能實現真正的同時執行。目前,NT的對稱多處理器結構(SMP)最多支援32個處理器。
Windows NT是佔先式多工作業系統,這意味著作業系統不必等待一個執行緒,它可主動將處理器讓給其它執行緒。在這種方式下,當一個執行緒已運行了配額的時間後,或出現搶先情況時,作業系統將中斷該執行緒。如圖所示:


執行緒轉讓CPU處理時間是被迫的。佔先式多工可以防止執行緒獨佔CPU,允許其它執行緒公平地分享CPU執行時間,這和16位Windows環境下的協作式多工有著本質的區別。在16位Windows環境下,如果一個程式進入無限迴圈,則其它應用程式可能永遠沒有機會執行;而在Windows NT環境下這種情況不會發生,相反,許多執行緒的執行部分都採用了迴圈掃描的結構。這也是實現了多執行緒的基礎。