歡迎來到作業系統系列,采用圖解 + 大白話的形式來講解,讓小白也能看懂,幫助大家快速科普入門。
本篇文章開始探秘使用者态與核心态,雖然一般面試不會問這個,但搞清楚這塊,對我們了解整個計算機系統是及其有意義的,這會讓你在今後的學習中豁然開朗,你肯定會發出:“啊,原來如此的感歎!”
内容大綱

小故事
張三是某科技公司的初級Java開發工程師(低權限),目前在15樓辦公碼代碼,公司提供的資源僅有一套電腦(使用者态),張三想着這一線的房價,倍感壓力山大,于是給自己定下一個目标,一定要做技術總監,在一線紮根, 奮鬥B張三,奮鬥5年終于當上了技術總監(高權限),之後張三搬到30樓,可以随時向資源部(系統調用)申請公司各種資源與擷取公司的機密資訊(核心态),所謂是走上人生巅峰。
通過這個故事,我們發現,低權限的資源範圍較小,高權限的資源範圍更大,所謂的「使用者态與核心态隻是不同權限的資源範圍」。
C P U 指令集權限
在說使用者态與核心态之前,有必要說一下
C P U 指令集
,指令集是 C P U 實作軟體指揮硬體執行的媒介,具體來說每一條彙編語句都對應了一條 C P U 指令
,而非常非常多的 C P U 指令
在一起,可以組成一個、甚至多個集合,指令的集合叫 C P U 指令集
。
同時
C P U 指令集
有權限分級,大家試想, C P U 指令集
可以直接操作硬體的,要是因為指令操作的不規範`,造成的錯誤會影響整個計算機系統的。好比你寫程式,因為對硬體操作不熟悉,導緻作業系統核心、及其他所有正在運作的程式,都可能會因為操作失誤而受到不可挽回的錯誤,最後隻能重新開機計算機才行。
而對于硬體的操作是非常複雜的,參數衆多,出問題的幾率相當大,必須謹慎的進行操作,對開發人員來說是個艱巨的任務,還會增加負擔,同時開發人員在這方面也不被信任,是以作業系統核心直接屏蔽開發人員對硬體操作的可能,都不讓你碰到這些
C P U 指令集
C P U 指令集
設定了權限,不同級别權限能使用的 C P U 指令集
是有限的,以 Inter C P U 為例,Inter把 C P U 指令集
操作的權限由高到低劃為4級: - ring 0
- ring 1
- ring 2
- ring 3
其中 ring 0 權限最高,可以使用所有
C P U 指令集
,ring 3 權限最低,僅能使用正常
C P U 指令集
,不能使用操作硬體資源的
C P U 指令集
,比如
I O
讀寫、網卡通路、申請記憶體都不行,Linux系統僅采用ring 0 和 ring 3 這2個權限。
高情商
- ring 0被叫做核心态,完全在作業系統核心中運作
- ring 3被叫做使用者态,在應用程式中運作
低情商
- 執行核心空間的代碼,具有ring 0保護級别,有對硬體的所有操作權限,可以執行所有
,通路任意位址的記憶體,在核心模式下的任何異常都是災難性的,将會導緻整台機器停機C P U 指令集
- 在使用者模式下,具有ring 3保護級别,代碼沒有對硬體的直接控制權限,也不能直接通路位址的記憶體,程式是通過調用系統接口(System Call APIs)來達到通路硬體和記憶體,在這種保護模式下,即時程式發生崩潰也是可以恢複的,在電腦上大部分程式都是在,使用者模式下運作的
使用者态與核心态
通關了C P U 指令集權限,現在再說使用者态與核心态就十分簡單了,使用者态與核心态的概念就是C P U 指令集權限的差別,程序中要讀寫
I O
,必然會用到 ring 0 級别的
C P U 指令集
,而此時 C P U 的指令集操作權限隻有 ring 3,為了可以操作ring 0 級别的
C P U 指令集
, C P U 切換指令集操作權限級别為 ring 0,C P U再執行相應的ring 0 級别的
C P U 指令集
(核心代碼),執行的核心代碼會使用目前程序的核心棧。
PS:每個程序都有兩個棧,分别是使用者棧與核心棧,對應使用者态與核心态的使用
使用者态與核心态的空間
在記憶體資源上的使用,作業系統對使用者态與核心态也做了限制,每個程序建立都會配置設定「虛拟空間位址」(不懂可以參考我的另一篇文章“15分鐘!一文幫小白搞懂作業系統之記憶體”),以Linux32位作業系統為例,它的尋址空間範圍是
4G
(2的32次方),而作業系統會把虛拟控制位址劃分為兩部分,一部分為核心空間,另一部分為使用者空間,高位的
1G
(從虛拟位址 0xC0000000 到 0xFFFFFFFF)由核心使用,而低位的
3G
(從虛拟位址 0x00000000 到 0xBFFFFFFF)由各個程序使用。
- 使用者态:隻能操作
範圍的低位虛拟空間位址0-3G
- 核心态:
範圍的虛拟空間位址都可以操作,尤其是對0-4G
範圍的高位虛拟空間位址必須由核心态去操作3-4G
- 補充:
部分大家是共享的(指所有程序的核心态邏輯位址是共享同一塊記憶體位址),是核心态的位址空間,這裡存放在整個核心的代碼和所有的核心子產品,以及核心所維護的資料3G-4G
每個程序的
4G
虛拟空間位址,高位
1G
都是一樣的,即核心空間。隻有剩餘的
3G
才歸程序自己使用,換句話說就是, 高位
1G
的核心空間是被所有程序共享的!
最後做個小結,我們通過指令集權限區分使用者态和核心态,還限制了記憶體資源的使用,作業系統為使用者态與核心态劃分了兩塊記憶體空間,給它們對應的指令集使用
使用者态與核心态的切換
相信大家都聽過這樣的話「使用者态和核心态切換的開銷大」,但是它的開銷大在那裡呢?簡單點來說有下面幾點
- 保留使用者态現場(上下文、寄存器、使用者棧等)
- 複制使用者态參數,使用者棧切到核心棧,進入核心态
- 額外的檢查(因為核心代碼對使用者不信任)
- 執行核心态代碼
- 複制核心态代碼執行結果,回到使用者态
- 恢複使用者态現場(上下文、寄存器、使用者棧等)
實際上作業系統會比上述的更複雜,這裡隻是個大概,我們可以發現一次切換經曆了「使用者态 -> 核心态 -> 使用者态」。
使用者态要主動切換到核心态,那必須要有入口才行,實際上核心态是提供了統一的入口,下面是Linux整體架構圖
從上圖我們可以看出來通過系統調用将Linux整個體系分為使用者态和核心态,為了使應用程式通路到核心的資源,如CPU、記憶體、I/O,核心必須提供一組通用的通路接口,這些接口就叫系統調用。
庫函數就是屏蔽這些複雜的底層實作細節,減輕程式員的負擔,進而更加關注上層的邏輯實作,它對系統調用進行封裝,提供簡單的基本接口給程式員。
Shell顧名思義,就是外殼的意思,就好像把核心包裹起來的外殼,它是一種特殊的應用程式,俗稱指令行。Shell也是可程式設計的,它有标準的Shell 文法,符合其文法的文本叫Shell腳本,很多人都會用Shell腳本實作一些常用的功能,可以提高工作效率。
最後來說說,什麼情況會導緻使用者态到核心态切換
- 系統調用:使用者态程序主動切換到核心态的方式,使用者态程序通過系統調用向作業系統申請資源完成工作,例如 fork()就是一個建立新程序的系統調用,系統調用的機制核心使用了作業系統為使用者特别開放的一個中斷來實作,如Linux 的 int 80h 中斷,也可以稱為軟中斷
- 異常:當 C P U 在執行使用者态的程序時,發生了一些沒有預知的異常,這時目前運作程序會切換到處理此異常的核心相關程序中,也就是切換到了核心态,如缺頁異常
- 中斷:當 C P U 在執行使用者态的程序時,外圍裝置完成使用者請求的操作後,會向 C P U 發出相應的中斷信号,這時 C P U 會暫停執行下一條即将要執行的指令,轉到與中斷信号對應的處理程式去執行,也就是切換到了核心态。如硬碟讀寫操作完成,系統會切換到硬碟讀寫的中斷處理程式中執行後邊的操作等。
關聯好文章推薦
- 15分鐘!一文幫小白搞懂作業系統之記憶體
- 程序、線程與協程傻傻分不清?一文帶你吃透!
關于我
Hi這裡是阿星,一個熱愛技術的93年Java程式猿,在公衆号 「程式猿阿星」 裡将會定期分享作業系統、計算機網絡、Java、分布式、資料庫等精品原創文章,2021,與您在 Be Better 的路上共同成長!。
非常感謝各位人才能 看到這裡,創作不易,文章有幫助可以「點個贊」或「分享與評論」,都是支援(莫要白嫖)!