系統呼叫
一支普通程式不能被信任去直接碰磁碟、送網路封包、或發放記憶體,因為它和其他每一支程式以及硬體本身共用這台機器。這些權力是保留給作業系統的。系統呼叫(system call)就是程式用來請求作業系統代它執行這類特權動作的受控閘道。它就像銀行櫃員的窗口:你不能自己走進金庫,但你可以走到窗口,請有授權的櫃員依規定為你取錢。
在機制上,系統呼叫不只是普通的函式呼叫,因為它跨越了一道保護邊界。處理器在受限的使用者模式(user mode)下執行你的程式碼,禁止危險操作。要發出系統呼叫,程式把一個識別請求的編號(read、write、open 等)與它的引數放進約定好的暫存器,再執行一道特殊指令(RISC-V 上的 ecall、x86 上的 syscall),陷入核心(trap into the kernel),把處理器切換到特權模式,並跳到一個固定的作業系統進入點。核心檢查這個請求,若允許就執行它,把結果放進一個暫存器,再把控制權交還給你在使用者模式下的程式。這次切換的成本是真實的,這就是為什麼系統呼叫明顯比普通呼叫昂貴。
系統呼叫之所以重要,是因為它正是「軟體不再能為所欲為、必須請求許可」的那道接縫。程式對外部世界所做的一切(檔案、網路、時間、開新行程、要更多記憶體)最終都得經過它們。它們也是資安的基石:使用者/核心邊界正是阻止一支有臭蟲或惡意的程式踐踏系統其餘部分的東西,這就是為什麼穿越它的唯一一扇門是這道狹窄而有人看守的門。多數程式設計者從不寫那道原始指令;他們呼叫底層替他們執行系統呼叫的函式庫函式。
在 RISC-V Linux 上把一個字串寫到螢幕,就是一次系統呼叫: li a7, 64 # 系統呼叫編號 64 = write li a0, 1 # 引數:檔案描述符 1(標準輸出) la a1, msg # 引數:那些位元組的位址 li a2, 13 # 引數:幾個位元組 ecall # 陷入核心;由它執行寫入 # 核心把寫入的位元組數放進 a0 回傳,回到使用者模式
把呼叫編號與引數放進暫存器,再用 ecall 陷入核心,由核心執行特權工作後返回。
系統呼叫不只是普通的函式呼叫:它從使用者模式跨進核心,因此昂貴得多,而這正是阻止程式彼此傷害的那道有人看守的邊界。