理解程式的執行
我們要知道CPU可以自由地訪問暫存器、記憶體。另外,程式是由作業系統執行的,所以作業系統能夠控制程式的所有執行情況,限制程式的行為。
程式地執行過程:
- 程式是一個二進位制檔案,包含程式的程式碼指令、程式碼中的文字資訊等(參考C語言的程式的各種段)
- 執行一個程式後,會將這個二進位制載入到記憶體中,那麼這個程式的程式碼(想象成各種彙編指令)也就記載道了記憶體中
- CPU執行程式時從固定的位置main處開始執行(eip暫存器指向這裡),逐條語句讀取執行(這是CPU自帶的功能)
- 語句可能發生跳轉(eip切換到其他彙編指令出)
- 語句可能會操作棧(其實就是往一塊特殊地記憶體空間寫入資料、讀出資料,CPU有相關的指令pop push解決這個問題)
- 程式可能會執行系統呼叫(作業系統賦予的能力,例如讀寫檔案,網路通訊等)。現代作業系統將這些能力都放到了核心態來執行了,即只有核心程式碼才能做實際的讀寫檔案操作,普通使用者程式只能透過系統呼叫來執行這些能力。
- 所以執行系統呼叫後,cpu就會相應地跳轉到系統呼叫地入口處(這個系統呼叫的入口也時固定的,對應的是核心中的一段C程式碼
- 核心的系統呼叫入口函式,根據系統呼叫號(對每個系統呼叫的標識),找到相應的處理函式執行(其實也是執行call函式)
- 系統呼叫處理完後,繼續返回到使用者自己的程式程式碼處執行(所以,在執行系統呼叫前需要把使用者程式碼執行的位置記錄下來,並且在系統呼叫結束後自動設定eip指向這個地方)
函式呼叫
C語言函式呼叫關鍵
c語言函式呼叫的幾個關鍵點在於:
- 保護呼叫者的上下文(暫存器、棧指標(ebp,esp)資訊)
- 將傳入引數透過esi、edi等放到被暫存器中、或者push到棧中(當引數比較多時)
- 執行call呼叫函式,call的副作用是將eip壓入到棧中
- 將計算的返回值放到eax中
- pop出ebp、esp
- 執行ret,將eip從棧中pop出來,然後指令繼續執行重新回到呼叫者上下文(將esp指向呼叫者呼叫函式後的語句)
系統呼叫
- syscall sysenter sysret
- int 0x80
在 x86-64 架構上,當應用程式需要執行系統呼叫時,CPU 會從使用者態切換到核心態,經歷以下過程:
- 使用者態程式執行
syscall
指令:- 使用者態程式透過執行
syscall
指令來觸發系統呼叫請求。
- 使用者態程式透過執行
- CPU 切換到核心態:
syscall
指令會引發一個特殊的異常,導致 CPU 從當前的使用者態特權級切換到核心態的更高特權級。- 這個過程會自動儲存使用者態的部分暫存器狀態,如
rip
、rflags
等,並將控制權轉交給核心。
- 核心處理系統呼叫:
- 核心接管控制權後,會根據系統呼叫號找到對應的系統呼叫處理函式,並執行相應的操作。
- 核心執行完成後,會將結果返回給使用者態程式。
- 從核心態切換回使用者態:
- 核心執行完系統呼叫處理後,會透過
sysret
指令從核心態切換回使用者態。 sysret
指令會自動恢復之前儲存的使用者態暫存器狀態,並將控制權轉回給使用者態程式。
- 核心執行完系統呼叫處理後,會透過
整個切換過程由硬體和作業系統核心共同完成,應用程式感知不到這個切換過程。這種基於硬體支援的使用者態 - 核心態切換機制,能夠大幅降低系統呼叫的開銷,提高作業系統的整體效能。
需要注意的是,除了 syscall/sysret
指令,Intel 的 x86-64 架構也支援使用 int 0x80
軟中斷來執行系統呼叫,不過 syscall/sysret
方式通常更加高效。
linux系統呼叫 - chenjx_ucs - 部落格園 (cnblogs.com)
cpu的特權級別(privilege level )
chatgpt告訴我們:Linux 系統透過以下幾種方式來實現當前特權級別的切換:
- 系統呼叫:
- 使用者態應用程式透過系統呼叫機制從 Ring 3 切換到 Ring 0 核心態。
- 應用程式執行
int 0x80
或syscall
指令觸發軟中斷,CPU 切換到 Ring 0 執行核心程式碼。 - 核心處理完成後,透過
iret
或sysret
指令返回到使用者態。
- 中斷/異常處理:
- 當 CPU 遇到硬體中斷或軟體異常時, 會自動從當前特權級切換到 Ring 0 核心態。
- 核心處理完中斷/異常後,透過
iret
指令返回到之前的特權級別。
- 特權級切換指令:
- x86 架構提供了一些用於特權級切換的指令,如
call gate
、task gate
等。 - 這些指令可以在不同特權級之間跳轉,並自動完成上下文切換。
- x86 架構提供了一些用於特權級切換的指令,如
- 程序切換:
- 當核心需要切換程序時,會切換程序的特權級別。
- 核心將新程序的特權級別設定為 Ring 3,並透過
iret
指令返回到使用者態。
在 Linux 中,大多數情況下都是透過系統呼叫和中斷/異常處理來實現特權級切換。核心程式碼執行在 Ring 0 級別,使用者態應用程式執行在 Ring 3 級別。當應用程式需要訪問受保護的系統資源時,會透過系統呼叫陷入核心態,由核心程式碼執行相應的操作。中斷和異常處理也會觸發核心態的切換,核心負責處理各種硬體事件。總之,Linux 系統利用 CPU 硬體提供的特權級機制,透過系統呼叫、中斷/異常處理、特權級切換指令等方式,實現了核心態和使用者態之間的特權級切換,保證了系統的安全和穩定性。
Ring 0和Ring 3也有其他區別,例如Ring 0 程式可以執行所有的 CPU 指令集,包括特權指令。Ring 3 程式只能執行非特權指令集,無法直接執行特權級別的指令。
參考資料:
使用者空間與核心空間,程序上下文與中斷上下文[總結] - Rabbit_Dale - 部落格園 (cnblogs.com)