本文主要講了以下問題:系統調用的概念、系統調用的實現原理與過程以及如何在 Linux 中增加一個系統調用。
系統調用是為了和用戶空間上的進程進行交互,內核提供的一組界面。
應用程序通過這組界面訪問硬件和其他操作系統資源
完成對硬件和資源的訪問控制
硬件設備的抽象(提供設備的獨立性)
fork(), exec(), open(), read(), write(), close(),……
目前 Linux 系統調用 300 多個
應用程序通過在用戶空間實現的 API 而不是直接通過系統調用來編程
例:調用 printf() 函數時,應用程序、C 庫和內核的關系:
應用程序調用 printf() -> C 庫中的 printf() -> C 庫中的 write() -> 內核中的 write() 系統調用
int 80H:軟中斷,通知內核的機制是靠軟中斷實現的,第128號中斷處理程序
IVT(Interrupt Vector Table):中斷向量表,包括所有中斷程序入口地址,它固定存放於內存中(實模式下應用)
IDT(Interrupt Descriptor Table):中斷描述符表,不固定內存位置,通過 IDTR 寄存器定位該表(保護模式下應用,int 80H 占據其中一項)
syscall table:系統調用表
系統調用號: 在 Linux 中,每個系統調用被賦予一個系統調用號,表示它在表中的編號
操作系統在加載時做的有關系統調用的加載:
int 80H 處理程序地址的加載:start_kernel()中的 trap_init()和 set_system_gate()
各系統調用處理程序的加載(entry.s)
首先,通過軟中斷陷入到 int 80h 中斷中,促使系統切換到內核態去執行異常處理程序(系統調用處理程序);之後,系統通過讀取 eax 寄存器的值來獲取系統調用號;之後,系統通過讀取寄存器來獲取傳遞的參數(ebx, ecx, edx, esi, edi)按照順序存放前五個參數,如果參數為6個或以上,則將其中一個寄存器的值指向內存空間;最後,執行相應系統調用代碼,完成系統調用
系統調用必須仔細檢查他們所有參數是否合法有效,如果用戶將不合法的參數傳遞給內核,那麼系統的安全和穩定將面臨極大考驗。
權限驗證:系統調用的調用者可以使用 capable() 函數來檢查是否有權能對制定的資源進行操作
指針合法性驗證:在接受一個用戶空間的指針之前,內核需要驗證:
指針指向的內存區域屬於用戶空間 指針指向的內存區域在進程的地址空間裡 如果是讀,該內存應被標記為可讀;如果是寫,該內存應被標記為可寫;如果是可執行,進程決不能繞過內存訪問限制增加系統調用函數(/kernel/sys.c)
把系統調用函數入口添加到 sys_call_table(entry.s)
添加系統調用號
它為用戶提供了一種硬件的抽象接口
在保證系統穩定和安全的前提下提供服務,避免應用程序恣意橫行