Linux系统调用FAQ
1. Linux系統調用的作用?
系統調用是操作系統為用戶態運行的進程與系統內核、硬件設備(如CPU、磁盤、打印機等)進行交互提供的一組接口,在應用程序和硬件之間設置一個額外層的優點包括:
1.???????? 用戶編程更加簡單,不必學習硬件設備的低級編程特性;
2.???????? 提高了系統的安全性,內核在試圖滿足某個請求前在接口級可以檢查請求正確性。
3.???????? 這組接口使得程序具有可移植性,只要內核所提供的接口相同,使用這些接口的?? ?????程序即可正確的編譯和執行。
2. POSIX API與系統調用的關系?
1.???????? API是函數定義(如libc庫),而系統調用是通過軟中斷想內核態發出的請求;
2.???????? 一個API沒有必要對應一個特定的系統調用。API可能直接提供用戶態服務,如數學函數;有些API函數可能調用幾個系統調用;幾個API函數可能調用同一系統調用(如malloc,calloc,free均使用brk系統調用實現)。
3.???????? POSIX標準針對API而不是針對系統調用,判斷一個系統是否與POSIX兼容,要看它是否提供了一組合適的API。
4.???????? 從應用程序設計者的角度看,兩者的差別僅在于名字、參數、返回值的不同;從內核設計者的角度看,系統調用屬于內核,而API不屬于內核。
?
3. 內核如何處理系統調用?
1.???????? 每一個系統調用擁有一個系統調用號的標識,當用戶態請求系統調用時,需傳遞系統調用號及其它信息作為參數,eax寄存器用于傳遞系統調用號(同時也用于傳遞返回值,系統調用號與返回值類型相同)。內核從寄存器讀到系統調用的參數,并執行對應的系統調用服務例程。
2.???????? 下圖顯示了應用程序、相應的封裝例程、系統調用處理程序、系統調用服務例程之間的關系。xyz()是應用程序的調用接口,xyz在libc中的調用實現通過調用SYSCALL匯編指令實現,該指令使得CPU切換到內核態(SYSEXIT反之);內核態通過sys_call系統調用處理程序來最終調用xyz的服務例程sys_xyz()。
???
3.???????? 為了將系統調用號與相應的服務例程關聯起來,內核利用一個系統調用分派表,該表存放在sys_call_table數組中,有NR_syscalls個表項,第n個表項包含系統調用號為n的服務例程的地址。
?
?
4. 如何進入和退出系統調用?
本地應用可以通過兩種方式調用系統調用:
1.???????? 執行int $0x80匯編語言指令(老版本linux中從用戶態切換到內核態的唯一方式)
2.???????? 執行sysenter匯編語言指令,Intel Pentium II微處理器引入該指令,linux2.6內核支持這條指令。
同樣,內核可通過兩種方式從系統調用退出,使CPU切換回到用戶態:
1.???????? 執行iret匯編語言指令
2.???????? 執行sysexit匯編語言指令
對以上兩種方式涉及到匯編代碼,我也解釋不太清楚,更詳細的了解,請參考《深入理解linux內核》。
?
5. 系統調用參數如何傳遞?
1.???????? 普通C函數的參數傳遞是通過把參數值寫入到活動的程序棧(用戶態或內核態堆棧)實現的,而系統調用同時跨越內核態和用戶態,同時操作兩個棧是很復雜的,而采用下面的方式(寄存器傳遞參數)將使得系統調用處理程序與其它異常處理程序的結構類似。
2.???????? 在發出系統調用之前,系統調用的參數被寫入到CPU的寄存器,然后在調用系統服務例程之前,內核再把存放在CPU中的參數拷貝到內核態堆棧(系統調用服務例程是普通的C函數)。
3.???????? 使用寄存器傳遞參數必須滿足:每個參數的長度不能超過寄存器的長度(容易解決,長度不夠,可以通過傳遞參數的地址解決),并且參數的個數不能超過6個(系統調用號除外),因為80x86處理器的寄存器的數量是有限的(用于存放調用號和調用參數的寄存器是eax、ebx、ecx、edx、esi、edi、ebp)。
?
6. 如何保證系統調用的安全性?
內核在滿足用戶的系統調用請求前,必須仔細的檢查所有的系統調用參數,如write系統調用的參數fd,內核需檢查fd是否對應于一個打開的文件,該文件是否允許寫操作等,這些檢查操作依賴于系統調用本身,也依賴于特定的參數。但有一種檢查對所有的系統調用是通用的,只要參數指定的是地址,內核必須檢查它是否屬于用戶地址空間,可通過檢查地址所在的線性區,或直接與PAGE_OFFSET對比等兩種方式實現(linux2.2開始實現第二種方式)。
?
7. 內核如何訪問用戶態數據?
系統調用服務例程需要非常頻繁的讀寫進程地址空間的數據,Linux提供一組宏來實現這個目的(__開頭的宏不包含對線性地址的有效性檢查)。
get_user? __get_user?? 從用戶空間讀一個整數(1、2或4個字節)
put_user? __put_user?? 向用戶空間寫一個整數(1、2或4個字節)
copy_from_user __copy_from_user? 從用戶空間復制任意大小的塊
copy_to_user? __copy_to_user? 把任意大小的塊復制到用戶空間
strncpy_from_user ?__strncpy_from_user? 從用戶空間復制一個以null結束的字符串
strlen_user? strnlen_user? 返回用戶空間以null結束的字符串的長度
clear_user __clear_user 用0填充用戶空間的一個內存區域
?
8. 在內核態能否調用系統調用?
盡管系統調用主要由內核態進程使用,但也可以被內核線程調用,內核線程不能使用庫函數。linux定義了7個從_syscall0到_syscall6的一組宏來封裝相應例程,宏名中的數字0-6對應著系統調用所用的參數個數(系統調用號除外)。每個宏需要2 + 2 * n個參數(n為系統調用參數的個數),前兩個參數是系統調用號返回值類型和名字;每一對附加參數指明相應的系統調用參數的類型和名字。
轉載于:https://www.cnblogs.com/yunnotes/archive/2013/04/19/3032426.html
總結
以上是生活随笔為你收集整理的Linux系统调用FAQ的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DWR入门教程
- 下一篇: 十大不变计算机网络安全法则