前面我們提到用戶級線程的切換,是通過切換線程棧來實現的,由TCB線程式控制制塊來控制,用戶自己就能實現,無需通過內核。這次我們來講講內核級線程以及它的切換。

內核級線程,顧名思義,就是處於內核態的線程,對於內核級線程,可以很好配合多核CPU,多核指的是MMU(映射)一樣,但是有多個核可以並行的處理事件。

這就是操作系統的進程線程示意圖,可以看到只有核心級線程可以很好的利用郵件條件,多核CPU等

回顧上次所說的用戶級線程的切換,是兩個棧之間的切換,那核心級線程的切換是什麼呢?不是兩個棧,而是兩套棧。因為核心級線程必須到內核態,在用戶態使用用戶棧,在內核態不也得調用函數,也得使用一個棧,既要在用戶態又得在內核態跑,一個核心級線程要兩個棧組成的一套棧。

對比用戶級線程的切換使用TCB進行切換,核心級線程也用TCB切換,不過這個TCB在內核中,而且根據TCB的切換來切換一套棧,內核棧和用戶棧都得切換。

那麼具體的如何從一個核心級線程切到另一個核心級線程呢?這就是大名鼎鼎的五段論了。首先我們得從當前的用戶棧切到內核棧,只有通過系統中斷纔可以進入內核,INT就是系統中斷,通過在內核棧裡面存儲SS,SP來記錄切換回來的指針,我們就可以切入內核棧了,如果想返回,可以調用IRET的系統調用返回,這就是一套棧了。

100A(){
B();
104;
}
200B(){
read();
204;
}
用戶程序300:read(){
| int 0x80;
| 304;
| }
| system_call;
內核程序call sys_call
1000;
2000:sys_read(){ }

如上圖,我們調用read()調用系統中斷進入內核態,現在的棧的情況是

用戶棧:104
204

內核棧:SS SP(記錄返回指針)
EFLAGS
304
CS
1000

可以看到這兩個棧配合的非常好,這就是一套棧的精髓所在,也完成了切換的第一段。

由於read需要讀磁碟,這會使線程進入堵塞階段,引發CPU調度,switch_to通過TCB的切換(第二段)找到另一個線程B的內核棧的指針,然後切到另一套棧(第三段),也就是線程的切換,這是切換的第二、三段。

但是我們執行的是用戶的函數,所以我們還得切到線程B的用戶棧(第四段),這裡我們執行完內核棧的函數,通過彈棧中斷返回到SS SP彈回線程B的用戶棧,這就是切換的第五段。

如果是進程的切換的話,我們另外切換地址映射表就可以了。

以上就是內核級線程切換的switch_to五段論。下次我們再講講內核級線程是如何是實現的,它的源碼是什麼樣的呢?以及後面綜合的內核級線程和用戶級線程的比較對照。

推薦閱讀:

相關文章