Linux使用了Ring3級別運行用戶態,Ring0做爲內核態,沒有使用Ring1和Ring2。Ring3狀態不能訪問Ring0的地址空間,包括代碼和數據。Linux進程的4GB地址空間,3G-4G部分你們是共享的,是內核態的地址空間,這裏存放在整個內核的代碼和全部的內核模塊,以及內核所維護的數據。用戶運行一個程序,該程序所建立的進程開始是運行在用戶態的,若是要執行文件操做,網絡數據發送等操做,必須經過write,send等系統調用,這些系統調用會調用內核中的代碼來完成操做,這時,必須切換到Ring0,而後進入3GB-4GB中的內核地址空間去執行這些代碼完成操做,完成後,切換回Ring3,回到用戶態。這樣,用戶態的程序就不能隨意操做內核地址空間,具備必定的安全保護做用。
保護模式,經過內存頁表操做等機制,保證進程間的地址空間不會互相沖突,一個進程的操做不會修改另外一個進程的地址空間中的數據。在內核態下,CPU可執行任何指令,在用戶態下CPU只能執行非特權指令。當CPU處於內核態,能夠隨意進入用戶態;而當CPU處於用戶態,只能經過中斷的方式進入內核態。通常程序一開始都是運行於用戶態,當程序須要使用系統資源時,就必須經過調用軟中斷進入內核態. html
1. 用戶態和內核態的概念區別 linux
究竟什麼是用戶態,什麼是內核態,這兩個基本概念之前一直理解得不是很清楚,根本緣由我的以爲是在於由於大部分時候咱們在寫程序時關注的重點和着眼的角度放在了實現的功能和代碼的邏輯性上,先看一個例子: 安全
1)例子 網絡
void testfork(){ if(0 = = fork()){ printf(「create new process success!\n」); } printf(「testfork ok\n」); }
這段代碼很簡單,從功能的角度來看,就是實際執行了一個fork(),生成一個新的進程,從邏輯的角度看,就是判斷了若是fork()返回的是0則打印相關語句,而後函數最後再打印一句表示執行完整個testfork()函數。代碼的執行邏輯和功能上看就是如此簡單,一共四行代碼,從上到下一句一句執行而已,徹底看不出來哪裏有體現出用戶態和進程態的概念。 數據結構
若是說前面兩種是靜態觀察的角度看的話,咱們還能夠從動態的角度來看這段代碼,即它被轉換成CPU執行的指令後加載執行的過程,這時這段程序就是一個動態執行的指令序列。而究竟加載了哪些代碼,如何加載就是和操做系統密切相關了。 架構
2)特權級 函數
熟悉Unix/Linux系統的人都知道,fork的工做其實是以系統調用的方式完成相應功能的,具體的工做是由sys_fork負責實施。其實不管是否是Unix或者Linux,對於任何操做系統來講,建立一個新的進程都是屬於核心功能,由於它要作不少底層細緻地工做,消耗系統的物理資源,好比分配物理內存,從父進程拷貝相關信息,拷貝設置頁目錄頁表等等,這些顯然不能隨便讓哪一個程序就能去作,因而就天然引出特權級別的概念,顯然,最關鍵性的權力必須由高特權級的程序來執行,這樣才能夠作到集中管理,減小有限資源的訪問和使用衝突。 spa
特權級顯然是很是有效的管理和控制程序執行的手段,所以在硬件上對特權級作了不少支持,就Intel x86架構的CPU來講一共有0~3四個特權級,0級最高,3級最低,硬件上在執行每條指令時都會對指令所具備的特權級作相應的檢查,相關的概念有CPL、DPL和RPL,這裏再也不過多闡述。硬件已經提供了一套特權級使用的相關機制,軟件天然就是好好利用的問題,這屬於操做系統要作的事情,對於Unix/Linux來講,只使用了0級特權級和3級特權級。也就是說在Unix/Linux系統中,一條工做在0級特權級的指令具備了CPU能提供的最高權力,而一條工做在3級特權級的指令具備CPU提供的最低或者說最基本權力。 操作系統
3)用戶態和內核態 code
如今咱們從特權級的調度來理解用戶態和內核態就比較好理解了,當程序運行在3級特權級上時,就能夠稱之爲運行在用戶態,由於這是最低特權級,是普通的用戶進程運行的特權級,大部分用戶直接面對的程序都是運行在用戶態;反之,當程序運行在0級特權級上時,就能夠稱之爲運行在內核態。
雖然用戶態下和內核態下工做的程序有不少差異,但最重要的差異就在於特權級的不一樣,即權力的不一樣。運行在用戶態下的程序不能直接訪問操做系統內核數據結構和程序,好比上面例子中的testfork()就不能直接調用sys_fork(),由於前者是工做在用戶態,屬於用戶態程序,而sys_fork()是工做在內核態,屬於內核態程序。
當咱們在系統中執行一個程序時,大部分時間是運行在用戶態下的,在其須要操做系統幫助完成某些它沒有權力和能力完成的工做時就會切換到內核態,好比testfork()最初運行在用戶態進程下,當它調用fork()最終觸發sys_fork()的執行時,就切換到了內核態。
2. 用戶態和內核態的轉換
1)用戶態切換到內核態的3種方式
a. 系統調用
這是用戶態進程主動要求切換到內核態的一種方式,用戶態進程經過系統調用申請使用操做系統提供的服務程序完成工做,好比前例中fork()實際上就是執行了一個建立新進程的系統調用。而系統調用的機制其核心仍是使用了操做系統爲用戶特別開放的一箇中斷來實現,例如Linux的int 80h中斷。
b. 異常
當CPU在執行運行在用戶態下的程序時,發生了某些事先不可知的異常,這時會觸發由當前運行進程切換處處理此異常的內核相關程序中,也就轉到了內核態,好比缺頁異常。
c. 外圍設備的中斷
當外圍設備完成用戶請求的操做後,會向CPU發出相應的中斷信號,這時CPU會暫停執行下一條即將要執行的指令轉而去執行與中斷信號對應的處理程序,若是先前執行的指令是用戶態下的程序,那麼這個轉換的過程天然也就發生了由用戶態到內核態的切換。好比硬盤讀寫操做完成,系統會切換到硬盤讀寫的中斷處理程序中執行後續操做等。
這3種方式是系統在運行時由用戶態轉到內核態的最主要方式,其中系統調用能夠認爲是用戶進程主動發起的,異常和外圍設備中斷則是被動的。
2)具體的切換操做
從觸發方式上看,能夠認爲存在前述3種不一樣的類型,可是從最終實際完成由用戶態到內核態的切換操做上來講,涉及的關鍵步驟是徹底一致的,沒有任何區別,都至關於執行了一箇中斷響應的過程,由於系統調用實際上最終是中斷機制實現的,而異常和中斷的處理機制基本上也是一致的,關於它們的具體區別這裏再也不贅述。關於中斷處理機制的細節和步驟這裏也不作過多分析,涉及到由用戶態切換到內核態的步驟主要包括:
[1] 從當前進程的描述符中提取其內核棧的ss0及esp0信息。
[2] 使用ss0和esp0指向的內核棧將當前進程的cs,eip,eflags,ss,esp信息保存起來,這個
過程也完成了由用戶棧到內核棧的切換過程,同時保存了被暫停執行的程序的下一
條指令。
[3] 將先前由中斷向量檢索獲得的中斷處理程序的cs,eip信息裝入相應的寄存器,開始
執行中斷處理程序,這時就轉到了內核態的程序執行了。
轉自: http://www.cnblogs.com/tangr206/articles/3092225.html