講講用戶空間和內(nèi)核空間
本文轉(zhuǎn)載自微信公眾號(hào)「安琪拉的博客」,作者安琪拉 。轉(zhuǎn)載本文請(qǐng)聯(lián)系安琪拉的博客公眾號(hào)。
最近在重新梳理Java 并發(fā)的知識(shí),這篇文章是為了后面講ReentrantLock做準(zhǔn)備的,先熱個(gè)身,隨著研究的深入,就來到了這里,要把一件事情講清楚,可以無限下鉆,就像物體->分子->原子->夸克,直到超出自己能理解的知識(shí)范疇。
前言
我們知道操作系統(tǒng)采用的是虛擬地址空間,以32位操作系統(tǒng)舉例,它的尋址空間為4G(2的32次方),這里解釋二個(gè)概念:
- 尋址: 是指操作系統(tǒng)能找到的地址范圍,32位指的是地址總線的位數(shù),你就想象32位的二進(jìn)制數(shù),每一位可以是0,可以是1,是不是有2的32次方種可能,2^32次方就是可以訪問到的最大內(nèi)存空間,也就是4G。
- 虛擬地址空間:為什么叫虛擬,因?yàn)槲覀儍?nèi)存一共就4G,但操作系統(tǒng)為每一個(gè)進(jìn)程都分配了4G的內(nèi)存空間,這個(gè)內(nèi)存空間實(shí)際是虛擬的,虛擬內(nèi)存到真實(shí)內(nèi)存有個(gè)映射關(guān)系。例如X86 cpu采用的段頁式地址映射模型。
操作系統(tǒng)將這4G可訪問的內(nèi)存空間分為二部分,一部分是內(nèi)核空間,一部分是用戶空間。
內(nèi)核空間是操作系統(tǒng)內(nèi)核訪問的區(qū)域,獨(dú)立于普通的應(yīng)用程序,是受保護(hù)的內(nèi)存空間。
用戶空間是普通應(yīng)用程序可訪問的內(nèi)存區(qū)域。
以linux操作系統(tǒng)為例,將最高的1G字節(jié)(從虛擬地址0xC0000000到0xFFFFFFFF),供內(nèi)核使用,稱為內(nèi)核空間,而將較低的3G字節(jié)(從虛擬地址0x00000000到0xBFFFFFFF),供各個(gè)進(jìn)程使用,稱為用戶空間。空間分配如下圖所示:
每個(gè)進(jìn)程可以通過系統(tǒng)調(diào)用進(jìn)入內(nèi)核,因此,Linux內(nèi)核由系統(tǒng)內(nèi)的所有進(jìn)程共享。于是,從具體進(jìn)程的角度來看,每個(gè)進(jìn)程可以擁有4G字節(jié)的虛擬空間。
區(qū)分內(nèi)核空間和用戶空間原因
其實(shí)早期操作系統(tǒng)是不區(qū)分內(nèi)核空間和用戶空間的,但是應(yīng)用程序能訪問任意內(nèi)存空間,如果程序不穩(wěn)定常常把系統(tǒng)搞崩潰,比如清除操作系統(tǒng)的內(nèi)存數(shù)據(jù)。后來覺得讓應(yīng)用程序隨便訪問內(nèi)存太危險(xiǎn)了,就按照CPU 指令的重要程度對(duì)指令進(jìn)行了分級(jí),指令分為四個(gè)級(jí)別:Ring0~Ring3 (和電影分級(jí)有點(diǎn)像),linux 只使用了 Ring0 和 Ring3 兩個(gè)運(yùn)行級(jí)別,進(jìn)程運(yùn)行在 Ring3 級(jí)別時(shí)運(yùn)行在用戶態(tài),指令只訪問用戶空間,而運(yùn)行在 Ring0 級(jí)別時(shí)被稱為運(yùn)行在內(nèi)核態(tài),可以訪問任意內(nèi)存空間。
用戶態(tài)的程序不能隨意操作內(nèi)核地址空間,這樣對(duì)操作系統(tǒng)具有一定的安全保護(hù)作用。
說說內(nèi)核態(tài)和用戶態(tài)
其實(shí)很清晰:當(dāng)進(jìn)程/線程運(yùn)行在內(nèi)核空間時(shí)就處于內(nèi)核態(tài),而進(jìn)程/線程運(yùn)行在用戶空間時(shí)則處于用戶態(tài)。
在內(nèi)核態(tài)下,進(jìn)程運(yùn)行在內(nèi)核地址空間中,此時(shí) CPU 可以執(zhí)行任何指令。運(yùn)行的代碼也不受任何的限制,可以自由地訪問任何有效地址,也可以直接進(jìn)行端口的訪問。
在用戶態(tài)下,進(jìn)程運(yùn)行在用戶地址空間中,被執(zhí)行的代碼要受到 CPU 的很多檢查,比如:進(jìn)程只能訪問映射其地址空間的頁表項(xiàng)中規(guī)定的在用戶態(tài)下可訪問頁面的虛擬地址。
我們來看下linux系統(tǒng)的整體結(jié)構(gòu):下面是內(nèi)核空間(寫成了內(nèi)存空間)
用戶態(tài)到內(nèi)核態(tài)的切換
如上圖所示,所有系統(tǒng)資源的管理都是在內(nèi)存空間進(jìn)行的,也就是在內(nèi)核態(tài)去做的,那我們應(yīng)用程序需要訪問磁盤,讀取網(wǎng)卡的數(shù)據(jù),新建一個(gè)線程都需要通過系統(tǒng)調(diào)用接口,完成從用戶態(tài)到內(nèi)存態(tài)的切換。
比如我們 Java 中需要新建一個(gè)線程,new Thread( Runnable ...) 之后調(diào)用start() 方法時(shí), 看Hotspot Linux 的JVM 源碼實(shí)現(xiàn),最終是調(diào)pthread_create 系統(tǒng)方法來創(chuàng)建的線程,這里會(huì)從用戶態(tài)切換到內(nèi)核態(tài)完成系統(tǒng)資源的分配,線程的創(chuàng)建。
當(dāng)一個(gè)任務(wù)(進(jìn)程)執(zhí)行系統(tǒng)調(diào)用而陷入內(nèi)核代碼中執(zhí)行時(shí),稱進(jìn)程處于內(nèi)核運(yùn)行態(tài)(內(nèi)核態(tài))
除了系統(tǒng)調(diào)用可以實(shí)現(xiàn)用戶態(tài)到內(nèi)核態(tài)的切換,還有別的方式嗎?有,軟中斷和硬中斷。
軟中斷是指進(jìn)程發(fā)生了異常事件;硬中斷就有很多種,例如時(shí)鐘周期、IO等。涉及的操作系統(tǒng)知識(shí)比較多,這里不展開講。