核心態,使用者態,目態,管態

hemeinvyiqiluoben發表於2018-01-24

轉自: http://blog.csdn.net/msdnwolaile/article/details/52671251


目態,管態

大多數計算機系統將CPU執行狀態分為目態與管態。CPU的狀態屬於程式狀態字PSW的一位。CPU交替執行作業系統程式和使用者程式。

管態又叫特權態,系統態或核心態。CPU在管態下可以執行指令系統的全集。通常,作業系統在管態下執行。 
目態又叫常態或使用者態。機器處於目態時,程式只能執行非特權指令。使用者程式只能在目態下執行,如果使用者程式在目態下執行特權指令,硬體將發生中斷,由作業系統獲得控制,特權指令執行被禁止,這樣可以防止使用者程式有意或無意的破壞系統。 
從目態轉換為管態的唯一途徑是中斷。 
從管態到目態可以通過修改程式狀態字來實現,這將伴隨這由作業系統程式到使用者程式的轉換。

特權級

特權級(Ring)也叫(hierarchical protection domains),有的也稱為使用者態(user mode)。它是一種機制來保護資料和阻止惡意行為(確保電腦保安)。電腦作業系統提供不同許可權訪問級別的資源。特權級分為4級,特權級0、1、2、3。 
在windows中只使用特權級0和特權級3。特權最高的一般是特權級0,可以直接操作硬體,如CPU和記憶體。一般作業系統和驅動執行在此級別下。特權級3是給一般的程式使用的,可以呼叫基本的CPU指令。在特權級三無法呼叫特權級0的指令,如果呼叫則顯示為非法指令。 
引用特權級的概念是為了保護計算機,一些危險指令只有作業系統可以執行,防止普通程式濫用其他程式的資源。如間諜軟體要想開啟攝像頭就必須向特權級0的驅動程式請求開啟,否則就不允許。 
中斷門、呼叫門之類的。當然,也可以直接寫一個驅動,因為驅動一般是ring0級別的,所以,只要驅動被載入,就意味著進入了ring0.

在系統的表象上看,我們開啟工作管理員,可以看到一個名稱為“System”的程式,這個事實上就是ring0的程式。所有的驅動都顯示為在這個程式中,所有的驅動中建立的系統執行緒都是在這個程式中。表面上看,好像是ring3的程式,事實上,它是工作在ring0的。

使用者空間與核心空間

  我們知道現在作業系統都是採用虛擬儲存器,那麼對32位作業系統而言,它的定址空間(虛擬儲存空間)為4G(2的32次方)。操心繫統的核心是核心,獨立於普通的應用程式,可以訪問受保護的記憶體空間,也有訪問底層硬體裝置的所有許可權。為了保證使用者程式不能直接操作核心,保證核心的安全,操心繫統將虛擬空間劃分為兩部分,一部分為核心空間,一部分為使用者空間。針對linux作業系統而言,將最高的1G位元組(從虛擬地址0xC0000000到0xFFFFFFFF),供核心使用,稱為核心空間,而將較低的3G位元組(從虛擬地址0x00000000到0xBFFFFFFF),供各個程式使用,稱為使用者空間。每個程式可以通過系統呼叫進入核心,因此,Linux核心由系統內的所有程式共享。於是,從具體程式的角度來看,每個程式可以擁有4G位元組的虛擬空間。空間分配如下圖所示: 
  這裡寫圖片描述 
  有了使用者空間和核心空間,整個linux內部結構可以分為三部分,從最底層到最上層依次是:硬體–>核心空間–>使用者空間。如下圖所示: 
  這裡寫圖片描述 
需要注意的細節問題:

(1) 核心空間中存放的是核心程式碼和資料,而程式的使用者空間中存放的是使用者程式的程式碼和資料。不管是核心空間還是使用者空間,它們都處於虛擬空間中。

(2) Linux使用兩級保護機制:0級供核心使用,3級供使用者程式使用。

核心態和使用者態

核心態與使用者態:

(1)當一個任務(程式)執行系統呼叫而陷入核心程式碼中執行時,稱程式處於核心執行態(核心態)。此時處理器處於特權級最高的(0級)核心程式碼中執行。當程式處於核心態時,執行的核心程式碼會使用當前程式的核心棧。每個程式都有自己的核心棧。

(2)當程式在執行使用者自己的程式碼時,則稱其處於使用者執行態(使用者態)。此時處理器在特權級最低的(3級)使用者程式碼中執行。當正在執行使用者程式而突然被中斷程式中斷時,此時使用者程式也可以象徵性地稱為處於程式的核心態。因為中斷處理程式將使用當前程式的核心棧。

核心態與使用者態的切換:

當程式執行在使用者態,此時無論發生中斷、異常、系統呼叫(本質是中斷),都會通過異常向量表進入系統狀態(核心態),執行完中斷服務程式時又恢復原狀,返回到使用者態。主要步驟:

[1] 從當前程式的描述符中提取其核心棧的ss0及esp0資訊。

[2] 使用ss0和esp0指向的核心棧將當前程式的cs,eip,eflags,ss,esp資訊儲存起來,這個過程也完成了由使用者棧到核心棧的切換過程,同時儲存了被暫停執行的程式的下一條指令。

[3] 將先前由中斷向量檢索得到的中斷處理程式的cs,eip資訊裝入相應的暫存器,開始執行中斷處理程式,這時就轉到了核心態的程式執行了。

其實寫過linux核心驅動程式的同學應該就知道,實現一個字元裝置驅動,在write方法和read方法中,核心態和使用者態之間的橋樑就是copy_to_user和copy_form_user這兩個介面,在高執行級別下,程式碼可以執行特權指令,訪問任意的實體地址,這種CPU執行級別就對應著核心態,而在相應的低階別執行狀態下,程式碼的掌控範圍會受到限制。只能在對應級別允許的範圍內活動。其實正好說明了這個問題,程式設計師或者軟體應用工程師在編寫應用程式去控制裝置驅動的時候,首先肯定是會開啟相應的檔案描述符,然後對相應的檔案描述符進行讀寫,ioctl,lseek之類的操作。當在應用層編寫程式即是屬於使用者態,在應用程式不能訪問任意的硬體實體地址,所以當使用者需要讀取檔案描述符的內的內容時,就需要呼叫read,當使用者需要寫資料進檔案描述符時,就需要用write,在使用者態呼叫這兩個介面,進而就會進行系統呼叫,產生相應的系統呼叫號,然後核心會根據系統呼叫號找到相應的驅動程式,此時系統就處在核心態中,在驅動程式中,首先進行驅動程式初始化,然後註冊,產生驅動程式最重要主裝置號和次裝置號。初始化的過程中由於對相應的read方法和wirte方法進行初始化,故使用者態執行read操作,就會進而使CPU產生異常,然後進入核心態找到相應的read,write當然也是一樣的。

究竟什麼是使用者態,什麼是核心態,這兩個基本概念以前一直理解得不是很清楚,根本原因個人覺得是在於因為大部分時候我們在寫程式時關注的重點和著眼的角度放在了實現的功能和程式碼的邏輯性上,先看一個例子:

1)例子

C程式碼

  1. void testfork(){

  2. if(0 = = fork()){

  3. printf(“create new process success!\n”);

  4. }

  5. printf(“testfork ok\n”);

  6. }

這段程式碼很簡單,從功能的角度來看,就是實際執行了一個fork(),生成一個新的程式,從邏輯的角度看,就是判斷了如果fork()返回的是則列印相關語句,然後函式最後再列印一句表示執行完整個testfork()函式。程式碼的執行邏輯和功能上看就是如此簡單,一共四行程式碼,從上到下一句一句執行而已,完全看不出來哪裡有體現出使用者態和程式態的概念。

如果說前面兩種是靜態觀察的角度看的話,我們還可以從動態的角度來看這段程式碼,即它被轉換成CPU執行的指令後載入執行的過程,這時這段程式就是一個動態執行的指令序列。而究竟載入了哪些程式碼,如何載入就是和作業系統密切相關了。

2)特權級

熟悉Unix/Linux系統的人都知道,fork的工作實際上是以系統呼叫的方式完成相應功能的,具體的工作是由sys_fork負責實施。其實無論是不是Unix或者Linux,對於任何作業系統來說,建立一個新的程式都是屬於核心功能,因為它要做很多底層細緻地工作,消耗系統的物理資源,比如分配實體記憶體,從父程式拷貝相關資訊,拷貝設定頁目錄頁表等等,這些顯然不能隨便讓哪個程式就能去做,於是就自然引出特權級別的概念,顯然,最關鍵性的權力必須由高特權級的程式來執行,這樣才可以做到集中管理,減少有限資源的訪問和使用衝突。

特權級顯然是非常有效的管理和控制程式執行的手段,因此在硬體上對特權級做了很多支援,就Intel x86架構的CPU來說一共有0~3四個特權級,0級最高,3級最低,硬體上在執行每條指令時都會對指令所具有的特權級做相應的檢查,相關的概念有 CPL、DPL和RPL,這裡不再過多闡述。硬體已經提供了一套特權級使用的相關機制,軟體自然就是好好利用的問題,這屬於作業系統要做的事情,對於 Unix/Linux來說,只使用了0級特權級和3級特權級。也就是說在Unix/Linux系統中,一條工作在級特權級的指令具有了CPU能提供的最高權力,而一條工作在3級特權級的指令具有CPU提供的最低或者說最基本權力。

3)使用者態和核心態

現在我們從特權級的排程來理解使用者態和核心態就比較好理解了,當程式執行在3級特權級上時,就可以稱之為執行在使用者態,因為這是最低特權級,是普通的使用者程式執行的特權級,大部分使用者直接面對的程式都是執行在使用者態;反之,當程式執行在級特權級上時,就可以稱之為執行在核心態。

雖然使用者態下和核心態下工作的程式有很多差別,但最重要的差別就在於特權級的不同,即權力的不同。執行在使用者態下的程式不能直接訪問作業系統核心資料結構和程式,比如上面例子中的testfork()就不能直接呼叫 sys_fork(),因為前者是工作在使用者態,屬於使用者態程式,而sys_fork()是工作在核心態,屬於核心態程式。

當我們在系統中執行一個程式時,大部分時間是執行在使用者態下的,在其需要作業系統幫助完成某些它沒有權力和能力完成的工作時就會切換到核心態,比如testfork()最初執行在使用者態程式下,當它呼叫fork()最終觸發 sys_fork()的執行時,就切換到了核心態。

  1. 使用者態和核心態的轉換

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://blog.sina.com.cn/s/blog_69f141290100ul02.html 
http://www.aichengxu.com/view/24604997


相關文章