系統調用
Linux內核中設置了一組用於實現各種系統功能的子程序,稱為系統調用。系統調用實際上是內核中的一些C函數,它們都以sys開頭的,如sys_mkdir()。它們通過一個指令int 0x80(軟中斷)把控制權交給內核,即進入特權級執行。int 0x80指令會使“執行”跳轉到系統調用在內核中定義的入口地址。這個位置是唯一確定的,且只可被用戶進程讀,不可寫,這正是利用了“陷阱們”跳轉的優點。
進程可以跳轉到的內核在的位置叫做system_call。它通過查找系統調用表sys_call_table,找到希望調用的內核函數的地址,調用此函數後返回。Linux裏面的每個系統調用是由一些宏,一張系統調用表,一個系統調用入口來完成的。
設定0x80號中斷
系統啟動後,初始化工作中較重要的一部分在start_kernel()函數中進行,函數start_kernel調用了函數trap_init並設置了各種中斷服務程序入口。與系統調用相關的是宏set_system_gate(0x80,&system_call),set_system_gate函數調用層次圖如下:
宏_set_gate()的作用是把addr地址值放入gate_addr所指向的內存單元中,使中斷向量表中的0x80項保存中斷服務程序system_call的入口地址。
系統調用現場保護
系統調用表sys_call_table,部分列出如下:
system_call是都有系統調用的入口。它的功能是保存所有寄存器,檢驗是否合法的系統調用,根據_sys_call_table中的偏移量把控制權轉給真正的系統調用代碼,系統調用完畢後調用_ret_from_sys_call(),返回到用戶空間。
與中斷一樣,當進入系統調用時要用到宏過程SAVE_ALL保護現場(保存寄存器)。當系統調用返回時,要調用RESTORE_ALL恢復現場。SAVE_ALL分析如下:
SAVE_ALL保存的一幀現場所有的寄存器,與該過程所要傳遞的pt_regs結構中的成員一致。
宏過程RESTORE_ALL與SAVE_ALL幾乎是實現完全相反的操作。
Linux系統調用的流程
1 系統調用過程
系統啟動後,經過引導和實模式下的初始化,進入保護模式下的核心初始化,執行head.s。其中的startup_32代碼段中調用Setup_idt。Setup_idt的功能是建立一個空的且具有256個項的中斷向量表。
接著系統轉入start_kernel()模塊。在該模塊中,調用trap_init()初始化中斷向量表,並且把系統調用system_call項註冊為0x80號中斷的中斷服務程序。
2 中斷INT 0x80入口處理
中斷INT 0x80入口system_call的匯編程序的主要功能是:
- 保存寄存器當前值(SAVE_ALL)
- 檢驗是否為合法的系統調用
- 根據系統調用表_sys_call_table和EAX持有的系統調用號找出並轉入系統調用響應函數;
- 從該響應函數返回後,讓EAX寄存器保存函數返回值,跳轉至ret_from_sys_call
- 最後,在執行位於用戶程序中系統調用命令後面余下的指令之前,若INT 0x80的返回值為非,則直接按類型type返回;否則,將INT 0x80的返回值取其絕對值,保留在errno變量中,返回-1.
system_call是整個系統調用公共入口部分。
系統調用