Linux 核心空間與使用者空間

sparkdev發表於2018-02-03

本文以 32 位系統為例介紹核心空間(kernel space)和使用者空間(user space)。

核心空間和使用者空間

對 32 位作業系統而言,它的定址空間(虛擬地址空間,或叫線性地址空間)為 4G(2的32次方)。也就是說一個程式的最大地址空間為 4G。作業系統的核心是核心(kernel),它獨立於普通的應用程式,可以訪問受保護的記憶體空間,也有訪問底層硬體裝置的所有許可權。為了保證核心的安全,現在的作業系統一般都強制使用者程式不能直接操作核心。具體的實現方式基本都是由作業系統將虛擬地址空間劃分為兩部分,一部分為核心空間,另一部分為使用者空間。針對 Linux 作業系統而言,最高的 1G 位元組(從虛擬地址 0xC0000000 到 0xFFFFFFFF)由核心使用,稱為核心空間。而較低的 3G 位元組(從虛擬地址 0x00000000 到 0xBFFFFFFF)由各個程式使用,稱為使用者空間。
對上面這段內容我們可以這樣理解:
每個程式的 4G 地址空間中,最高 1G 都是一樣的,即核心空間。只有剩餘的 3G 才歸程式自己使用。
換句話說就是, 最高 1G 的核心空間是被所有程式共享的!
下圖描述了每個程式 4G 地址空間的分配情況(此圖來自網際網路):

為什麼需要區分核心空間與使用者空間

在 CPU 的所有指令中,有些指令是非常危險的,如果錯用,將導致系統崩潰,比如清記憶體、設定時鐘等。如果允許所有的程式都可以使用這些指令,那麼系統崩潰的機率將大大增加。
所以,CPU 將指令分為特權指令和非特權指令,對於那些危險的指令,只允許作業系統及其相關模組使用,普通應用程式只能使用那些不會造成災難的指令。比如 Intel 的 CPU 將特權等級分為 4 個級別:Ring0~Ring3。
其實 Linux 系統只使用了 Ring0 和 Ring3 兩個執行級別(Windows 系統也是一樣的)。當程式執行在 Ring3 級別時被稱為執行在使用者態,而執行在 Ring0 級別時被稱為執行在核心態。

核心態與使用者態

好了我們現在需要再解釋一下什麼是核心態、使用者態:
當程式執行在核心空間時就處於核心態,而程式執行在使用者空間時則處於使用者態。
在核心態下,程式執行在核心地址空間中,此時 CPU 可以執行任何指令。執行的程式碼也不受任何的限制,可以自由地訪問任何有效地址,也可以直接進行埠的訪問。
在使用者態下,程式執行在使用者地址空間中,被執行的程式碼要受到 CPU 的諸多檢查,它們只能訪問對映其地址空間的頁表項中規定的在使用者態下可訪問頁面的虛擬地址,且只能對任務狀態段(TSS)中 I/O 許可點陣圖(I/O Permission Bitmap)中規定的可訪問埠進行直接訪問。

對於以前的 DOS 作業系統來說,是沒有核心空間、使用者空間以及核心態、使用者態這些概念的。可以認為所有的程式碼都是執行在核心態的,因而使用者編寫的應用程式程式碼可以很容易的讓作業系統崩潰掉。
對於 Linux 來說,透過區分核心空間和使用者空間的設計,隔離了作業系統程式碼(作業系統的程式碼要比應用程式的程式碼健壯很多)與應用程式程式碼。即便是單個應用程式出現錯誤也不會影響到作業系統的穩定性,這樣其它的程式還可以正常的執行(Linux 可是個多工系統啊!)。

所以,區分核心空間和使用者空間本質上是要提高作業系統的穩定性及可用性。

如何從使用者空間進入核心空間

其實所有的系統資源管理都是在核心空間中完成的。比如讀寫磁碟檔案,分配回收記憶體,從網路介面讀寫資料等等。我們的應用程式是無法直接進行這樣的操作的。但是我們可以透過核心提供的介面來完成這樣的任務。
比如應用程式要讀取磁碟上的一個檔案,它可以向核心發起一個 "系統呼叫" 告訴核心:"我要讀取磁碟上的某某檔案"。其實就是透過一個特殊的指令讓程式從使用者態進入到核心態(到了核心空間),在核心空間中,CPU 可以執行任何的指令,當然也包括從磁碟上讀取資料。具體過程是先把資料讀取到核心空間中,然後再把資料複製到使用者空間並從核心態切換到使用者態。此時應用程式已經從系統呼叫中返回並且拿到了想要的資料,可以開開心心的往下執行了。
簡單說就是應用程式把高科技的事情(從磁碟讀取檔案)外包給了系統核心,系統核心做這些事情既專業又高效。

對於一個程式來講,從使用者空間進入核心空間並最終返回到使用者空間,這個過程是十分複雜的。舉個例子,比如我們經常接觸的概念 "堆疊",其實程式在核心態和使用者態各有一個堆疊。執行在使用者空間時程式使用的是使用者空間中的堆疊,而執行在核心空間時,程式使用的是核心空間中的堆疊。所以說,Linux 中每個程式有兩個棧,分別用於使用者態和核心態。

下圖簡明的描述了使用者態與核心態之間的轉換:

既然使用者態的程式必須切換成核心態才能使用系統的資源,那麼我們接下來就看看程式一共有多少種方式可以從使用者態進入到核心態。概括的說,有三種方式:系統呼叫、軟中斷和硬體中斷。這三種方式每一種都涉及到大量的作業系統知識,所以這裡不做展開。

整體結構

接下來我們從核心空間和使用者空間的角度看一看整個 Linux 系統的結構。它大體可以分為三個部分,從下往上依次為:硬體 -> 核心空間 -> 使用者空間。如下圖所示(此圖來自網際網路):

在硬體之上,核心空間中的程式碼控制了硬體資源的使用權,使用者空間中的程式碼只有透過核心暴露的系統呼叫介面(System Call Interface)才能使用到系統中的硬體資源。其實,不光是 Linux,Windows 作業系統的設計也是大同小異。

實際上我們可以將每個處理器在任何指定時間點上的活動概括為下列三者之一:

  • 執行於使用者空間,執行使用者程式。
  • 執行於核心空間,處於程式上下文,代表某個特定的程式執行。
  • 執行於核心空間,處於中斷上下文,與任何程式無關,處理某個特定的中斷。

以上三點幾乎包括所有的情況,比如當 CPU 空閒時,核心就執行一個空程式,處於程式上下文,但執行在核心空間。
說明:Linux 系統的中斷服務程式不在程式的上下文中執行,它們在一個與所有程式都無關的、專門的中斷上下文中執行。之所以存在一個專門的執行環境,就是為了保證中斷服務程式能夠在第一時間響應和處理中斷請求,然後快速地退出。

總結

現代的作業系統大都透過核心空間和使用者空間的設計來保護作業系統自身的安全性和穩定性。所以在我們閱讀有關作業系統的資料時經常遇到核心空間、使用者空間和核心態、使用者態等概念,希望本文能夠幫助您理解這些基本的概念。

參考:

使用者空間與核心空間,程式上下文與中斷上下文[總結]
核心態與使用者態詳解!!!!!

相關文章