【java併發編程】底層原理——用戶態和內核態的區別

一、背景--線程狀態切換的代價

java的線程是映射到操作系統原生線程之上的,如果要阻塞或喚醒一個線程就需要操作系統介入,需要在戶態與核心態之間切換,這種切換會消耗大量的系統資源,因爲用戶態與內核態都有各自專用的內存空間,專用的寄存器等,用戶態切換至內核態需要傳遞給許多變量、參數給內核,內核也需要保護好用戶態在切換時的一些寄存器值、變量等,以便內核態調用結束後切換回用戶態繼續工作。

synchronized會導致爭用不到鎖的線程進入阻塞狀態,所以說它是java語言中一個重量級的同步操縱,被稱爲重量級鎖,爲了緩解上述性能問題,JVM從1.5開始,引入了輕量鎖與偏向鎖,默認啓用了自旋鎖,他們都屬於樂觀鎖。所以明確java線程切換的代價,是理解java中各種鎖的優缺點的基礎之一。

 

二、用戶態和內核態的概念

2.1內核態

CPU可以訪問內存所有數據, 包括外圍設備, 例如硬盤, 網卡. CPU也可以將自己從一個程序切換到另一個程序

2.2用戶態

只能受限的訪問內存, 且不允許訪問外圍設備. 佔用CPU的能力被剝奪, CPU資源可以被其他程序獲取

由於需要限制不同的程序之間的訪問能力, 防止他們獲取別的程序的內存數據, 或者獲取外圍設備的數據, 併發送到網絡, CPU劃分出兩個權限等級 :用戶態內核態 

 

 

三、用戶態與內核態的切換

所有用戶程序都是運行在用戶態的, 但是有時候程序確實需要做一些內核態的事情, 例如從硬盤讀取數據, 或者從鍵盤獲取輸入等.,而唯一可以做這些事情的就是操作系統, 所以此時程序就需要先操作系統請求以程序的名義來執行這些操作(比如java的I/O操作底層都是通過native方法來調用操作系統)。

這時需要一個這樣的機制: 用戶態程序切換到內核態, 但是不能控制在內核態中執行的指令。

這種機制叫系統調用, 在CPU中的實現稱之爲陷阱指令(Trap Instruction)

他們的工作流程如下:

  1. 用戶態程序將一些數據值放在寄存器中, 或者使用參數創建一個堆棧(stack frame), 以此表明需要操作系統提供的服務.
  2. 用戶態程序執行陷阱指令
  3. CPU切換到內核態, 並跳到位於內存指定位置的指令, 這些指令是操作系統的一部分, 他們具有內存保護, 不可被用戶態程序訪問
  4. 這些指令稱之爲陷阱(trap)或者系統調用處理器(system call handler). 他們會讀取程序放入內存的數據參數, 並執行程序請求的服務
  5. 系統調用完成後, 操作系統會重置CPU爲用戶態並返回系統調用的結果

當一個任務(進程)執行系統調用而陷入內核代碼中執行時,我們就稱進程處於內核運行態(或簡稱爲內核態)。此時處理器處於特權級最高的(0級)內核 代碼中執行。當進程處於內核態時,執行的內核代碼會使用當前進程的內核棧。每個進程都有自己的內核棧。當進程在執行用戶自己的代碼時,則稱其處於用戶運行 態(用戶態)。即此時處理器在特權級最低的(3級)用戶代碼中運行。當正在執行用戶程序而突然被中斷程序中斷時,此時用戶程序也可以象徵性地稱爲處於進程 的內核態。因爲中斷處理程序將使用當前進程的內核棧。這與處於內核態的進程的狀態有些類似。

我們在系統中執行一個程序時,大部分時間是運行在用戶態下的,在其需要操作系 統幫助完成某些它沒有權力和能力完成的工作時就會切換到內核態,比如前面提到的Java程序阻塞和喚醒一個線程時就需要切換到內核態。

補充:內核態與用戶態在操作系統中的詳細執行過程

內核態與用戶態是操作系統的兩種運行級別, 跟intel cpu沒有必然的聯繫, intel cpu提供Ring0-Ring3三種級別的運行模式,Ring0級別最高,Ring3最低。Linux使用了Ring3級別運行用戶態,Ring0作爲 內核態,沒有使用Ring1和Ring2。Ring3狀態不能訪問Ring0的地址空間,包括代碼和數據。Linux進程的4GB地址空間,3G-4G部 分大家是共享的,是內核態的地址空間,這裏存放在整個內核的代碼和所有的內核模塊,以及內核所維護的數據。用戶運行一個程序,該程序所創建的進程開始是運 行在用戶態的,如果要執行文件操作,網絡數據發送等操作,必須通過write,send等系統調用,這些系統調用會調用內核中的代碼來完成操作,這時,必 須切換到Ring0,然後進入3GB-4GB中的內核地址空間去執行這些代碼完成操作,完成後,切換回Ring3,回到用戶態。這樣,用戶態的程序就不能 隨意操作內核地址空間,具有一定的安全保護作用。
至於說保護模式,是說通過內存頁表操作等機制,保證進程間的地址空間不會互相沖突,一個進程的操作不會修改另一個進程的地址空間中的數據。

 

四、用戶態切換到內核態的3種方式

4.1 系統調用

這是用戶態進程主動要求切換到內核態的一種方式,用戶態進程通過系統調用申請使 用操作系統提供的服務程序完成工作,比如print()實際上就是執行了一個輸出的系統調用。而系統調用的機制其核心還是使用了操作系統爲用戶 特別開放的一箇中斷來實現,例如Linux的int 80h中斷。

4.2 異常

當CPU在執行運行在用戶態下的程序時,發生了某些事先不可知的異常,這時會觸發由當前運行進程切換到處理此異常的內核相關程序中,也就轉到了內核態,比如缺頁異常。

4.3 外圍設備的中斷

當外圍設備完成用戶請求的操作後,會向CPU發出相應的中斷信號,這時CPU會 暫停執行下一條即將要執行的指令轉而去執行與中斷信號對應的處理程序,如果先前執行的指令是用戶態下的程序,那麼這個轉換的過程自然也就發生了由用戶態到 內核態的切換。比如硬盤讀寫操作完成,系統會切換到硬盤讀寫的中斷處理程序中執行後續操作等。

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章