用戶空間進程和硬件設備之間經過系統調用來交互,其主要做用有三個。html
系統調用是用戶空間訪問內核的惟一手段;除異常和陷入外,是內核惟一合法的入口。linux
應用程序經過在用戶空間實現的應用編程接口(API)而非直接經過系統調用來編程。程序員
POSIX是應用編程接口的一個國際標準,C庫提供了POSIX的絕大部分API。編程
Unix接口設計的特色:提供機制(須要實現什麼功能)而非策略(怎樣實現這些功能)。Unix系統調用抽象出了用於完成某種肯定的目的的函數,而至於函數是如何實現功能的則並不關心,從程序員的角度來看,只需經過接口即可實現功能。api
要訪問系統調用,一般經過C庫定義的函數調用來進行。例如,getpid()系統調用,在內核中的實現爲:安全
SYSCALL_DEFINED0(getpid) { return task_tgid_vnr(current);//return current->tgid }
SYSCALL _ DEFINED0只是一個宏,定義一個無參數的系統調用,展開後的代碼以下函數
asmlinkage long sys_getpid(void)
asmlinkage爲限定詞,是一個編譯指令,通知編譯器僅從棧中提取該函數的參數。函數返回值在用戶態時爲int,在內核態爲long。性能
在Linux系統中每一個系統調用被賦予一個系統調用號,當用戶空間的進程執行一個系統調用時,系統調用號用來指明執行哪一個系統調用學習
系統調用號一旦分配就不會再更改,被刪除的系統調用號也不準再回收。設計
sys _ ni _ syscall()專門針對無效的系統調用而設立的,只負責返回-ENOSYS。
系統調用號被定義在arch/i386/kernel/syscall_64.c文件中。
X86中,系統調用號經過eax寄存器傳遞給內核,system _ call()函數經過將給定的系統調用與NR _syscalls做比較來檢查其有效性,若是大於或等於NR _syscalls就返回-ENOSYS,不然就執行相應的系統調用:
call *sys_call_table(,%eax,8)//基址+偏移量*8
上一篇博客中有詳細的記錄,這裏再也不贅述。見http://www.cnblogs.com/July0207/p/5277774.html
因爲系統調用在內核空間執行,因此必須驗證其參數是否合法有效,並且必須是正確的。
檢查用戶提供的指針是否有效,在接收這個指針以前,必須保證:
內核提供了兩個方法來完成必須的檢查內核空間與用戶空間數據的來回拷貝。
爲了向用戶空間寫數據,內核提供了copy _ to _user(),它須要三個參數,第一個是進程空間中的目的內存地址,第二個是內核空間內的源地址,第三個是須要拷貝的數據長度(字節數)。
爲了從用戶空間讀數據,內核提供了copy _ from _user(),該函數把第二個參數指定位置上的數據拷貝到第一個參數的指定位置,第三個是須要拷貝的數據長度(字節數)。
若是運行成功,則返回0,若是失敗,則返回沒能拷貝成功的字節數。
調用capable()函數檢查用戶是否有權對指定資源進行操做,返回非0值則有權限,返回0無權限。
<linux/capability.h>中包含一份全部權能和其對應的權限列表。
內核在執行系統調用時處於進程上下文。在進程上下文中,內核能夠:
Linux自己提供了一組宏,用於直接對系統調用進行訪問。他會設置好寄存器並調用陷入指令。該宏必須瞭解到底有多少參數按照怎樣的順序壓入寄存器。
_syscalln() //n的範圍從0到6,表明須要傳遞給系統調用的參數個數。
例如,open()系統調用的形式是:
long open(const char *filename, int flags, int mode) 等價於 #define NR_open 5 _syscall3(long,open,const char*,filename, int,flags, int,mode)
對於每一個宏來講,都有(2+2xn)個參數:
1.系統調用的返回值類型
2.系統調用的名稱
3及之後是按照系統調用參數的順序排列每一個參數的類型和名稱。
_ NR _open在<asm/unistd.h>中定義。這個宏會被擴展成爲內嵌彙編的C函數。
好處:
問題:
替代方法: