陷入核心(模式切換)(trapping into the kernel)
回到旅館櫃台。你不能就這樣繞到櫃台後面走進後台辦公室——中間隔著一道檯面。你只能按一個員工們約好會回應的鈴;鈴一響,某位特定的員工會走到某個特定的位置、依某套特定的程序,這時才處理你的請求。陷入核心正是這個鈴:一個刻意的單一信號,把控制權交到核心事先選好的地方。
以下是逐步的機制。程式把一個系統呼叫編號放進一個暫存器(說明它要哪項服務)、把引數放進其他暫存器。接著它執行一條特殊指令——在現代 x86-64 上是 syscall 指令;早期程式則用像 int 0x80 這樣的軟體中斷。那條指令一次做好幾件事:把 CPU 從使用者模式切換到核心模式、存下程式原本的位置好讓它能回來、並跳到開機時就註冊好的固定核心進入點。程式無法挑選自己降落在哪裡;它一律降落在核心可信的處理常式裡。核心接著讀那個編號、分派給對的常式、完成工作、把回傳值放進一個暫存器,再執行一條「從核心返回」指令,切回使用者模式並讓程式從陷阱之後繼續執行。這整段受控的轉移就叫做陷阱(trap)。
為何重要:這道固定、單向的門,正是讓特權之牆保持安全的關鍵。程式無法跳到核心內部的任意位址——它只能按鈴,並降落在核心為它準備好的地方。這也說明了為什麼系統呼叫比單純的函式呼叫昂貴:它牽涉到保存狀態與切換模式,而不只是一次跳躍。這裡的「陷阱」一詞指的是由程式刻意執行的某條指令所造成、刻意的、同步的切換——有別於從外部抵達的中斷(interrupt),也有別於用來表示錯誤的故障(fault)。
在 Linux x86-64 上,一次 write() 歸結為:把 write 的系統呼叫編號放進 rax 暫存器、檔案描述符放進 rdi、緩衝區位址放進 rsi、長度放進 rdx,然後執行 syscall 指令。CPU 陷入核心,核心完成寫入並把寫出的位元組數放回 rax 回傳。
編號與引數放進暫存器;一條指令完成模式切換。
陷阱是同步且刻意的——是程式執行某條指令主動要求的。別把它和硬體中斷(非同步、來自裝置)或故障(CPU 表示某條指令出了錯)搞混。這三者都把控制權轉給核心,但原因不同。