windows核心程式設計--纖程

Mobidogs發表於2020-04-04
比執行緒更小的單位,好像用的不多的哦


纖程的操作

首先要注意的一個問題是,實現執行緒的是Wi n d o w s核心。作業系統清楚地知道執行緒的情況,並且根據M i c r o s o f t定義的演算法對執行緒進行排程。纖程是以使用者方式程式碼來實現的,核心並不知道纖程,並且它們是根據使用者定義的演算法來排程的。由於你定義了纖程的排程演算法,因此,就核心而言,纖程採用非搶佔式排程方式。

需要了解的下一個問題是,單執行緒可以包含一個或多個纖程。就核心而言,執行緒是搶佔排程的,是正在執行的程式碼。然而,執行緒每次執行一個纖程的程式碼—你決定究竟執行哪個纖程(隨著我們講解的深入,這些概念將會越來越清楚)。

當使用纖程時,你必須執行的第一步操作是將現有的執行緒轉換成一個纖程。可以通過呼叫C o n v e r t T h r e a d To F i b e r函式來執行這項操作:

 

PVOID ConvertThreadToFiber(PVOID pvParam);
該函式為纖程的執行環境分配相應的記憶體(約為2 0 0位元組)。該執行環境由下列元素組成:

• 一個使用者定義的值,它被初始化為傳遞給C o n v e r t T h r e a d To F i b e r的p v P a r a m引數的值。 

• 結構化異常處理鏈的頭。 

• 纖程記憶體棧的最高和最低地址(當將執行緒轉換成纖程時,這也是執行緒的記憶體棧)。 

• CPU暫存器,包括堆疊指標、指令指標和其他。

當對纖程的執行環境進行分配和初始化後,就可以將執行環境的地址與執行緒關聯起來。該執行緒被轉換成一個纖程,而纖程則在該執行緒上執行。C o n v e r t T h r e a d To F i b e r函式實際上返回纖程的執行環境的記憶體地址。雖然必須在晚些時候使用該地址,但是決不應該自己對該執行環境資料進行讀寫操作,因為必要時纖程函式會為你對該結構的內容進行操作。現在,如果你的纖程(執行緒)返回或呼叫E x i t T h r e a d函式,那麼纖程和執行緒都會終止執行。

除非打算建立更多的纖程以便在同一個執行緒上執行,否則沒有理由將執行緒轉換成纖程。若要建立另一個纖程,該執行緒(當前正在執行纖程的執行緒)可以呼叫C r e a t e F i b e r函式:

 

PVOID CreateFiber(
   DWORD dwStackSize,
   PFIBER_START_ROUTINE pfnStartAddress,
   PVOID pvParam);
C r e a t e F i b e r首先設法建立一個新記憶體棧,它的大小由d w S t a c k S i z e引數來指明。通常傳遞的引數是0,按照預設設定,它建立一個記憶體棧,其大小可以擴充套件為1 M B,不過開始時有兩個儲存器頁面用於該記憶體棧。如果設定一個非0值,那麼就用設定的大小來儲存和使用記憶體棧。

接著,C r e a t e F i b e r函式分配一個新的纖程執行環境結構,並對它進行初始化。該使用者定義的值被設定為傳遞給C r e a t e F i b e r的p v P a r a m引數的值,新記憶體棧的最高和最低地址被儲存,同時,纖程函式的記憶體地址(作為p f n S t a r t A d d r e s s引數來傳遞)也被儲存。

P f n S t a r t A d d r e s s引數用於設定必須實現的纖程例程的地址,它必須採用下面的原型:

 

VOID WINAPI FiberFunc(PVOID pvParam);
當纖程被初次排程時,該函式就開始執行,並且將原先傳遞給C r e a t e F i b e r的p v P a r a m的值傳遞給它。可以在這個纖程函式中執行想執行的任何操作。但是該函式的原型規定返回值是V O I D,這並不是因為返回值沒有任何意義,而是因為該函式根本不應該返回。如果纖程確實返回了,那麼執行緒和該執行緒建立的所有纖程將立即被撤消。

與C o n v e r t T h r e a d To F i b e r函式一樣,C r e a t e F i b e r函式也返回纖程執行環境的記憶體地址。但是,與C o n v e r t T h r e a d To F i b e r不同的是,這個新纖程並不執行,因為當前執行的纖程仍然在執行。在單個執行緒上,每次只能執行一個纖程。若要使新纖程能夠執行,可以呼叫Switch To Fiber函式:

 

VOID SwitchToFiber(PVOID pvFiberExecutionContext);
Switch To Fiber 函式只有一個引數,即p v F i b e r E x e c u t i o n C o n t e x t,它是上次呼叫C o n v e r t T h r e a d To F i b e r或C r e a t e F i b e r函式時返回的纖程的執行環境的記憶體地址。該記憶體地址告訴該函式要對哪個纖程進行排程。S w i t c h To F i b e r函式在內部執行下列操作步驟:

1) 它負責將某些當前的C P U暫存器儲存在當前執行的纖程執行環境中,包括指令指標暫存器和堆疊指標暫存器。

2) 它將上一次儲存在即將執行的纖程的執行環境中的暫存器裝入C P U暫存器。這些暫存器包括堆疊指標暫存器。這樣,當執行緒繼續執行時,就可以使用該纖程的記憶體棧。

3) 它將纖程的執行環境與執行緒關聯起來,執行緒執行特定的纖程。

4) 它將執行緒的指令指標設定為已儲存的指令指標。執行緒(纖程)從該纖程上次執行的地方開始繼續執行。

S w i t c h To F i b e r函式是纖程獲得C P U時間的唯一途徑。由於你的程式碼必須在相應的時間顯式呼叫S w i t c h To F i b e r函式,因此你對纖程的排程可以實施全面的控制。記住,纖程的排程與執行緒排程毫不相干。纖程執行所依賴的執行緒始終都可以由作業系統終止其執行。當執行緒被排程時,當前選定的纖程開始執行,而其他纖程則不能執行,除非顯式呼叫S w i t c h To F i b e r函式。若要撤消纖程,可以呼叫D e l e t e F i b e r函式:

 

VOID DeleteFiber(PVOID pvFiberExecutionContext);
該函式用於刪除p v F i b e r E x e c u t i o n C o n t e x t引數指明的纖程,當然這是纖程的執行環境的地址。該函式能夠釋放纖程棧使用的記憶體,然後撤消纖程的執行環境。但是,如果傳遞了當前與執行緒相關聯的纖程地址,那麼該函式就在內部呼叫E x i t T h r e a d函式,該執行緒及其建立的所有纖程全部被撤消。

D e l e t e F i b e r函式通常由一個纖程呼叫,以便刪除另一個纖程。已經刪除的纖程的記憶體棧將被撤消,纖程的執行環境被釋放。注意,纖程與執行緒之間的差別在於,執行緒通常通過呼叫E x i t T h r e a d函式將自己撤消。實際上,用一個執行緒呼叫Te r m i n a t e T h r e a d函式來終止另一個執行緒的執行,是一種不好的方法。如果你確實呼叫了Te r m i n a t e T h r e a d函式,系統並不撤消已經終止執行的執行緒的記憶體棧。可以利用纖程的這種能力來刪除另一個纖程,後面介紹示例應用程式時將說明這是如何實現的。

為了使操作更加方便,還可以使用另外兩個纖程函式。一個執行緒每次可以執行一個纖程,作業系統始終都知道當前哪個纖程與該執行緒相關聯。如果想要獲得當前執行的纖程的執行環境的地址,可以呼叫G e t C u r r e n t F i b e r函式:

 

PVOID GetCurrentFiber();
另一個使用非常方便的函式是G e t F i b e r D a t a:

 

PVOID GetFiberData();
前面講過,每個纖程的執行環境包含一個使用者定義的值。這個值使用作為C o n v e r t T h r e a dTo F i b e r或C r e a t e F i b e r的p v P a r a m引數而傳遞的值進行初始化。該值也可以作為纖程函式的引數來傳遞。G e t F i b e r D a t a只是檢視當前執行的纖程的執行環境,並返回儲存的值。

無論G e t C u r r e n t F i b e r還是G e t F i b e r D a t a,執行速度都很快,並且通常是作為內蘊函式(infrinsic funcfion)來實現的,這意味著編譯器能夠為這些函式生成內聯程式碼。

 

相關文章