線程與進程
- 進程是資源分配的基本單位,線程是調度的基本單位。
- 進程 = 資源 + 指令執行序列,如果一個進程中有多個指令執行序列(類似多個函數),可以認為這就是多個線程。即多個線程是共享進程的資源的。通過切換線程既實現了並發,又避免了進程切換時需要切換映射表的代價。映射表實際上是用來將虛擬內存映射到真實物理內存的。
- 因此線程的切換實質上就是映射表不變而改變PC指針。
- 每個線程都有自己的棧,即線程棧。
線程的價值
- 線程即多個執行序列共享一個地址空間
用戶級線程

- 用戶級線程本質上是不經過內核的,感覺像是用自定義的函數Yield()來模擬調度,自己控制線程的切換。即用戶級線程本質上就是一系列函數集合。
- 如上每個方框中都是一個用戶級線程的代碼,每個線程都有一個線程控制塊TCB,每個線程都有自己的函數調用棧,棧的地址就存放在線程TCB.esp中。
- 從第一個線程,即地址100開始執行,執行到函數B則把下一個地址104壓入線程1的函數棧
- 然后跳轉到B開始執行,執行到Yield函數開始切換線程,如圖中Yield函數的定義,注意esp是函數棧頂指針寄存器,即當前函數執行結束后會接着執行esp出棧的地址。圖中示意的是線程2中的Yield函數,從線程2切換到線程1需要做兩個操作,第一步是將當前棧頂指針寄存器esp中的內容保存到線程2的TCB中,即TCB2.esp中。第二步是將線程1的TCB1.esp放到cpu的寄存器esp中。即用戶級線程的切換本質上就是切換TCB和每個線程的棧(也在TCB中存着)。
- 注意圖中線程2的Yield函數中藍線圈出來的部分需要刪除,因為不需要手動跳轉到204,只要函數Yield執行完畢(到右括號),棧頂指針寄存器就會執行出棧,並從出棧元素處開始執行,此時棧頂指針寄存器中的棧頂元素就是204,因此可以直接從204開始執行。換言之,如果在Yield中jmp語句不刪去,那么會先跳轉到204處執行,然后將線程1的B函數執行完畢后會接着從執行函數棧頂元素處的代碼,此時棧頂是204,,,於是相當於執行了兩個204處的代碼。
- 用戶級線程不經過內核,少了進出內核的消耗,因此效率較高,但是如果某個線程要與硬件交互,比如網卡IO,則會引起對應的進程阻塞,此時cpu不會切換到其它線程去執行,而是直接切換到了另一個進程,如果此時沒有其它進程,則直接導致cpu空轉。因此即使通過用戶級線程啟動了多個任務序列,但一旦在內核中阻塞了,則啟動這多個序列的並發性就沒有了效果。
- 內核級線程的TCB都在內核中,因此不用自己寫Yield,而是由操作系統自動調度。
內核級線程
-
內核級線程能更好利用多核處理器,多核與多CPU的區別如下,多CPU有多個緩存和MMU,多核處理器則是使用同一套緩存和MMU。內核級線程中各個線程可以執行在不同的核上,從而實現並行。並發是同時出發交替執行,並行則是同時執行。
-
內核級線程相比用戶級線程的本質區別有兩點,一點是內核級線程的TCB在內核中,由內核負責切換。第二點是兩個用戶級線程需要兩個用戶(函數)棧,而兩個內核級線程則需要使用兩套棧,即每個TCB包含兩個棧,一個用戶棧一個內核棧,當切換核心機線程即切換TCB時會同時切換用戶棧和內核棧,不過這都是操作系統的活啦。
-
用戶線程通過中斷陷入內核,然后把用戶棧的一些相關信息壓入內核棧,內核線程通過IRET指令返回用戶態,並將內核棧中的相關信息出棧。
內核級線程切換5段論
- 因為本質上我們寫的程序都是應用程序,只有遇到跟硬件交互等任務時才會陷入內核,因此五段論最開始都是在用戶棧開始的。
- 第一段,用戶態程序調用中斷,進入切換。
- 第二段,中斷處理,比如啟動磁盤讀或時鍾中斷等,這會引發TCB切換,即線程切換。
- 第三段,tcb切換,從一個線程到另一個線程。
- 第四段,內核棧切換,從一個線程的內核棧,切換另一個線程的內核棧。
- 第五段,中斷出口,從內核棧切換回用戶棧。
- 在用戶看來,就是兩個線程的切換,內核中內核棧的切換是操作系統完成的,具體細節不可見。