作者:世至其美
幫忙進博客點擊廣告,支持一下
UNIX/LINUX,1. 系統調用
內核提供的用戶空間和內核進行交互的一組接口,應用程序受限地訪問硬件設備,提供創建新線程并與已有的進程進行通信,也提供了申請操作系統其他資源的能力。
1.2 系統調用形式
centos7各版本內核、asmlinkage long sys_getpid(void)
asmlinkage限定詞是一個編譯指令,通知編譯器僅從棧中提取該函數的參數,每個系統調用都需要這個限定詞。
為了兼容32位和64位系統,函數返回long。
linux內核開發,1.3 用戶空間訪問內核方式
系統調用
異常
陷入
1.4 系統調用作用
為用戶空間提供了一個硬件的抽象接口,應用程序不需要管磁盤的類型和硬件介質,甚至文件系統類型。
保證系統的穩定和安全,內核可基于權限、用戶類型和其他規則對需要進行的訪問進行裁決。
保證系統可以實現虛擬內存和多任務,應用程序若是隨意訪問硬件內核不知道,那無法保證每個程序都運行在虛擬系統。
1.5 系統調用號
每個系統調用對應一個系統調用號,進程不會提及系統調用的名稱,通過系統調用號指明是哪個系統調用。Linux系統中的sys_ni_syscall()是專門針對無效的系統調用而設立,如果一個系統調用被刪除或者不可用,這個函數將負責“填補空缺“。內核將所有的已注冊過的系統調用和系統調用號,存儲在sys_call_table中。
2. 系統調用性能
Linux系統調用比其他許多操作系統執行得要快,得益于以下三個方面簡潔高效的設計。
進出內核的上下文切換
系統調用處理程序
系統調用本身
2.1系統調用處理程序
內核運行在受保護的地址空間,用戶空間的應用程序無法直接執行內核代碼, 而是通過軟中斷通知內核去執行系統調用的機制。軟中斷是通過引發一個異常來促使系統切換到內核態去執行異常處理程序。在x86系統預定義的軟中斷的中斷號是128,通過int $0x80指令觸發該中斷,該指令觸發一個異常導致系統切換到內核態并執行第128號異常處理程序,即系統調用處理程序system_call()。sysenter指令比int中斷指令更快、更專業陷入內核。
2.1.1指定恰當的系統調用
在x86-64中,系統調用號是通過eax寄存器傳遞給內核,用戶空間將需要的系統調用號放入eax寄存器,系統調用處理程序(system_call)一旦運行就從eax寄存器中獲取系統調用號。 system_call()函數通過從eax寄存器獲取的系統調用號與NR_syscalls進行比較來檢查其有效性,當它大于或等于NR_syscalls,函數返回-ENOSYS,否則執行相應的系統調用call *sys_call_table(, %rax, 8)。由于系統調用表的表項是以64位(8字節)存放,所以需要將給定的系統調用號乘以4。
2.1.2參數傳遞
系統調用的參數傳遞也是需要放在寄存器,在x86-32系統中,參數存儲在ebx、ecx、edx、esi和edi寄存器,超過五個或以上, 存儲在一個單獨的寄存器指向所有參數在用戶空間地址的指針。
2.1.3參數驗證
系統調用必須檢查傳遞的參數是否合法有效,最重要的一種檢查是檢查用戶提供的指針是否有效。
內核必須保證:
指針指向的是用戶空間,進程不能讓內核去讀內核的空間的數據。
指針指向的是進程的地址空間, 進程不能讓內核去讀其他進程的數據。
進程不能繞過內存訪問限制,內存的可讀、可寫和可執行。
內核和用戶空間之間的數據來回拷貝,如下兩個函數:
copy_to_user()函數:向用戶空間寫入數據
copy_from_user()函數: 從用戶空間讀取數據
注意:copy_to_user()和copy_from_user()可能引起阻塞,當用戶數據缺頁被換出到硬盤,而不在物理內存中。線程會進入休眠狀態, 直到缺頁處理程序將硬盤重新換回到物理內存。
最后,將針對檢查是否有合法權限。
在老版本Linux內核中,需要超級用戶權限的系統調用是通過suser()函數來檢查用戶是否為超級用戶。
現在,采用更細粒度的權能機制對特殊資源進行權限管理,全部的權能操作在可找到,包含系統能夠理解的所有權能,在不修改內核源代碼情況下,驅動工程師和系統管理員就無法定義新的權能。內核專門為許可管理使用權能并導出了兩個系統調用capget和capset。
2.2系統調用上下文
內核執行系統調用時處于進程上下文,current指針指向當前任務,即引發系統調用的那個進程。在進程上下文中,內核是可以休眠(例如系統調用阻塞和顯性調用schedule())并且可以被搶占。當被擁有相同系統調用的進程搶占,必須保證系統調用是可重入的。
當系統調用返回時,控制權還是在system_call()中,它最終會負責切換到用戶空間,并讓用戶進程繼續執行下去。
2.2.1向內核綁定一個系統調用
在系統調用表中添加表項,一般是在entry.s文件,每一個硬件架構都會有一個單獨系統調用表,不同硬件架構的系統調用號對應系統調用是不一樣的,每個支持該系統調用的硬件架構都得往系統調用表中添加表項,系統調用在該表的位置即為系統調用號,從0開始遞增。
對于所支持的各種體系結構, 系統調用號都必須定義在。
系統調用必須編譯進去內核映象(不能編譯成模塊),只要放在kernel/下的相關文件即可。
2.2.2從用戶空間訪問系統調用
Linux本身提供了一組宏,直接可以訪問系統調用,它會設置好寄存器并調用陷入指令。這些宏是__syscalln(),n的范圍從0到6,代表傳遞給系統調用的個數。
例如:
long open(const char *filename, int flag, int mode)
用戶空間通過宏直接調用open()系統調用的形式為:
#define NR_open 5
//對于宏來說, 參數個數為2+2*n
__syscall3(long,open, const char*, filename,int,flags,int,mode)
3. 實現并調用一個系統調用
先將系統調用添加到所支持的硬件架構的系統調用表。例如sys_foo在系統調用表中第400的位置。
.long sys_foo
在中加入系統調用號。
#define __NR_foo 400
系統調用必須編譯進去內核映象,例如sys_foo()放在中。
#include
/*
* sys_foo
* 返回每個進程的內核棧大小
*/
asmlinkage long sys_foo(void)
{
return THREAD_SIZE;
}
應用程序在用戶空間中調用這個系統調用,前三步驟是在內核中實現一個系統調用。
#define __NR_foo 400
__syscall0(long,foo)
int main()
{
long stack_size;
stack_size = foo();
return 0;
}
作者:世至其美
幫忙進博客點擊廣告,支持一下
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态