看板 b97902HW 關於我們 聯絡資訊
簡介 Kernel Mode 與 User Mode 的概念 前言 今天鄭卜壬的系程提到了 Kernel Mode 與 User Mode 的概念。這 個概念和 Operating System 高度相關,不過這對大多數的同學來說 應該是非常陌生的。第一次聽到的同學可能不太能夠完全搞懂這個機 制是怎麼 work 的。我因為一些機緣先修了作業系統,加上我的組語 期未也是做相關的主題,所以我寫了這一篇文章,希望能幫助大家進 一步認識 Kernel Mode 與 User Mode。 為什麼要有 Kernel Mode 與 User Mode? 首先我們先從為什麼要有 Kernel Mode 與 User Mode 談起。大家桌 機或者是筆電用的作業系統,不論是 Windows 或者是 Linux,都可以 在同一個時間執行很多程式。 然而,當我們可以執行多個程式,我們如何確保二個程式不會相互干 擾?我們如何確保 A 使用者的程式不會更動 B 使用者程式的變數? 我們如何確保一個使用者不會佔著資源不放手(CPU、RAM...)?我們 如何確保一個使用者的程式不會惡意的更動作業系統內部的資料結構 以竊取更多的資源?我們如何確保硬體可以被正確地操作? 為了避免一個使用者的程式修改其他使用者的程式甚至是系統核心, 並且更進一步,讓作業系統可以壟斷所有的硬體資源,大部分的機 器(或者 CPU)至少會有二個執行特權(privilege):Kernel mode (又稱 System mode) 與 User mode。 以 x86 為例:一般是使用 ring 0 做為 Kernel mode,ring 0 擁有最 多的特權,可以直接操縱所有的硬體,包括可以存取所有的實體記憶體 位址、處理鍵盤中斷..等;而 User mode 的部分,一般是使用 ring 3 做為 User mode,不但所有存取記憶體的行為都會有邊界檢查(用硬體 實作的,所以很快),而且很多 ring 0 專用的指令也都不能執行,例 如:在 User mode 你就不能執行關機指令。 以上廢話這麼多,看不懂沒有關係,反正 OS 會再學一次,現在只要 記住以下三點就可以了: 1. 之所以要有 Kernel mode 和 User mode 之分,是因為我們希望 作業系統可以壟斷所有的硬體操作,讓一般的程式不能亂搞。 2. Kernel mode 就是萬能的,只要是 CPU 能管的硬體,Kernel mode 的程式就可以透過 machine code 來操作該硬體。 3. User mode 基本上就是「受限」的模式。除了一些沒有傷害的行 為之外什麼都不能做。 從 Kernel Mode 到 User Mode 如果 Kernel mode 可以操作硬體,那麼在 User mode 執行的指令如 何執行一些需要特權的指令呢?例如:關機需要特權、寫入檔案需要 特權、輸入輸出要特權、另外多要更多記憶體空間也要特權,沒有特 權的 User mode 程序(process)要怎麼做這些工作呢? 這時我們就要使用作業系統的 System call。不過在討論 System call 之前,請容我先介紹中斷(或者例外)的概念,因為他和 System call 息息相關。 中斷 Interrupt 不知道大家有沒有想過鍵盤的電子訊號如何變成標準輸入(stdin)的 字元?或者大家有沒有想過作業系統如何知道一個程式的指標有問題 進而顯示「本程式即將要關閉」?其實以上二個例子,中斷(Interrupt) 都是幕後的功臣! 中斷(Interrupt),是機器的一個「特別狀態」,當中斷產生時,正 在執行的工作會暫停下來,若干個必要的 register 也會被儲存到記 憶體,CPU 會先執行對應的 Exception handler。如果中斷的接收者 不是其他的硬體,CPU 就會執行作業系統提供的 Exception Handler。 例如:當我們按下鍵盤的時候,鍵盤的控制晶片會將按鍵的信號編碼 為中斷,CPU 收到中斷之後會停下手邊的工作,改為呼叫作業系統的 鍵盤中斷處理函式,該函式會再把按鍵的組合解碼為我們熟知的 ASCII 讓我們的程式讀取。 而「本程式即將要關閉」的道理也是一樣的,當硬體在檢查 access 的時候發現 address 有問題的時候,就會觸發中斷,而作業系統的 Exception Handler 在接收到中斷之後就會終止程式。 在 Exception Handler 執行時,其一定會進到 Kernel mode,因為 Exception Handler 算是作業系統的一部分,所以理所當然地應該 要有 Kernel mode 的特權。這樣 Exception Handler 才可以修 改對應的資料結構。 軟體中斷 當然,事實上不是只有硬體能產生中斷,我們也可以透過 instruction 來觸發中斷,我們稱之為軟體中斷。在不同的機器上,我們對能產生軟 體中斷的指令也有不同的稱呼,在 x86 我們就是使用 int,在 mips 就是使用 syscall,當然還有一些機器是叫做 trap。 而軟體中斷就是 System Call 可以從 User mode 跳到 Kernel mode 的祕密。一般的作業系統都會提供一個用 Assembly 寫的,但是可以 和 C object file 連結在一起的函式庫。 為了可以和 C object file 連結在一起,這個 Assembly 寫的函式庫 會符合一些調用規範(Calling Convention),以便和 C 的函式呼叫 銜接在一起。 還記得在組合語言課堂中所學到的 Function call 與 Stack 嗎? 在 Jump 到一個函式之前,我們必須先把引數(argument) 等等推入堆 疊,再呼叫 Jump 指令跳到該函式。對於 C compiler 而言,他們也要 做類似的事情,當 Compiler 看到以下的函式: extern void logan_write(void const *, int); int main() { logan_writes("hello world\n", 12); } 其中一種編譯結果就是: main: pushl %ebp movl %esp, %ebp andl $-16, %esp subl $16, %esp movl $12, 4(%esp) movl $.LC0, (%esp) call logan_write movl $0, %eax leave ret ps. 當選用的 Calling Convention 不同,會產生不同的結果。參見 wikipedia: x86 calling convention。 除此之外,Compiler 什麼都不會做。接下來是作業系統開發人員 的工作。這部分就要使用組合語言來撰寫。這裡的函式必需設定 register 的值,然後觸發軟體中斷。 例如我自己亂寫的函式:(使用 GAS syntax 與 Linux System Call) .globl logan_write logan_write: movl $4, %eax ; Linux 系統呼叫: Write movl $1, %ebx ; File descriptor (stdout) movl $LOGANSTR, %ecx ; 字串 movl $7, %edx ; 字串長度 int $0x80 ; 觸發軟體中斷 (進入作業系統) movl $4, %eax movl $1, %ebx movl 4(%esp), %ecx movl 8(%esp), %edx int $0x80 ; 觸發軟體中斷 (進入作業系統) ret 如同老師在上課所說的,在 MS-DOS 之中,如果要做系統呼叫,就必 需設定好 register 之後使用 int 21 觸發軟體中斷,讓作業系統接 手之後的工作。當然,作業系統接手之後,Privilege 就會從 User mode 晉升到 Kernel mode,進而讓沒有特權的程式可以執行被作業系 統壟斷的一些指令。 最後,如果 Exception Handler 執行完畢,視中斷的種類,有些會 return 到原來中斷發生前的同一個指令,有些會 return 到下一個 指令。當然,一旦所有的 Exception Handler 執行完畢,特權會從 Kernel mode 降回 User mode,因此在 User mode 執行的程式是無 法用 System Call API 之外的方法得到 Kernel mode 的執行機會。 結語 我們從 Kernel/User mode 的目的開始,一路說到 User mode 的程 式如何進行系統呼叫。希望大家看完之後,可以對這些跳來跳去的流 程有進一步的認識。 :-) -- LoganChien ----- from PTT2 個板 logan ----- -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 140.112.247.159 ※ 編輯: LoganChien 來自: 140.112.247.159 (02/24 21:35) ※ 編輯: LoganChien 來自: 140.112.247.159 (02/24 22:49)
fereshte:推!! 02/24 21:45
purplebleed:推一個 02/24 21:51
kate2008:push 02/24 21:53
hanabi:讚!! 02/24 21:57
Bingojkt:推@w< 02/24 22:02
iForests:推~ 02/24 22:11
qcl:推! 02/24 22:12
benck:推 02/24 22:38
clywin123:大推 02/24 22:40
davidpanda:推 02/24 22:41
※ 編輯: LoganChien 來自: 140.112.247.159 (02/24 23:01)
andy74139:推!! 02/24 23:08
※ 編輯: LoganChien 來自: 140.112.247.159 (02/24 23:58)
vanillaXleft:推!! 02/24 23:55
※ 編輯: LoganChien 來自: 140.112.247.159 (02/25 00:00) ※ 編輯: LoganChien 來自: 140.112.247.159 (02/25 00:01)
hrs113355:推!! 02/25 00:16
demundo:推! 02/25 00:17
Daniel1147:推 02/25 00:19
※ 編輯: LoganChien 來自: 140.112.30.84 (02/25 08:31)
godgunman:推! 02/25 08:36
r44:推! 02/25 08:47
Allen624:感謝分享 02/25 11:28
Hseuler:推~ 02/25 13:51
iownthegame:推一個 02/28 23:32
averangeall:有看有推 03/02 13:10
sn6783:有看有推 03/03 00:22
wens:其實現在x86都是用 syscall/sysenter 這個指令了 03/03 00:42
wens:無聊可以去翻glibc source code 03/03 00:45
wens:sysdeps/unix/sysv/linux/i386/sysdep.h 找 ENTER_KERNEL 03/03 00:46
wens:或 sysdeps/unix/sysv/linux/x86_64/syscall.S 03/03 00:46
LoganChien:謝謝學長。我有空會去看看。 03/08 14:11
LoganChien:-) 03/08 14:14
dennis2030:有看有推 對不起我現在才靜下心看完他QQ 03/10 01:05
penut85420: 推推 10/13 15:13