linux內核設計與實現一書閱讀整理 之第五章

CHAPTER 5 系統調用

5.1 與內核通訊

  • 系統調用在用戶空間進程和硬件設備之間添加了一箇中間層,該層主要做用有三個:linux

    • 爲用戶空間提供了一種硬件的抽象接口
    • 系統調用保證了系統的穩定和安全
    • 每一個進程都運行在虛擬系統中,而在用戶空間和系統的其他部分提供這樣一層公共接口。
  • 在Linux中,系統調用是用戶空間訪問內核的惟一手段:除異常和陷入外,它們是內核惟一的合法入口。編程

5.2 API、POSIX和C庫

通常狀況下,應用程序經過在用戶空間實現的應用編程接口(API)而不是直接經過系統調用來編程。安全

  • 系統調用的支持方式:函數

    1.系統調用依靠C庫支持。用戶程序經過包含標準頭文件並和C庫連接,就可使用系統調用。性能

    2.C庫也實現了Unix系統的主要API。此外,C庫還提供了POSIX的大部分API。學習

  • API、POSIX、C庫以及系統調用之間的關係:操作系統

  • 在Unix世界中,最流行的應用編程接口是基於POSIX標準的。翻譯

5.3 系統調用


5.3.1 系統調用

- 要訪問系統調用(syscall),一般經過C庫中定義的函數調用來進行。
 - 系統調用經過一個long類型的返回值來表示成功或者錯誤。一般,但也不絕對,用一個負的返回值來代表錯誤。返回一個0值一般代表成功。系統調用在出現錯誤的時候C庫會把錯誤碼寫入errno全局變量,經過調用perror()庫函數,能夠把該變量翻譯成用戶能夠理解的錯誤字符串。
 - 系統調用在用戶空間和內核空間有不一樣的返回值類型,在用戶空間爲int,在內核空間爲long

5.3.2 系統調用號

- 當用戶空間的進程執行一個系統調用時,就用系統調用號指明到底執行哪一個系統調用。進程不會說起系統調用的名稱。
 - 系統調用號獨一無二,一旦分配就不能再有任何變動。不然編譯好的應用程序就會崩潰。
 - Linux有一個「未實現」系統調用sys_ni_syscall(),它除了返回-ENOSYS外不作任何其餘工做,這個錯誤號就是專門針對無效的系統調用而設的。
 - 內核記錄了系統調用表中的全部已註冊過的系統調用的列表,存儲在sys_call_table中。

5.3.3. 系統調用的性能

- Linux系統調用比其餘許多操做系統執行得要快。 - 緣由:設計

- 上下文切換時間短。
       - 系統調用處理程序和每一個系統調用自己也都很是簡潔。

5.4 系統調用處理程序

5.4.1 指定恰當的系統調用

  • 通知內核的機制是軟中斷實現的:經過引起一個異常來促使系統切換到內核態去指向異常處理程序,而此時的異常處理程序就是系統調用的處理程序。
  • 在x86系統上預約義的軟中斷是中斷號128,經過int $0x80指令觸發該中斷。
  • 用戶程序沒法直接執行內核代碼,它們也不能直接調用內核空間中的代碼

5.4.2 參數傳遞

  • 在陷入內核態以前,用戶空間就把相應的系統調用號傳給eax;這樣系統調用處理程序一旦運行,就能夠從eax中獲得數據
  • 在x86—32系統上,ebx,ecx,edx,esi和edi按順序存放前五個參數。須要6個及以上參數,應用一個單獨的寄存器存放指向這些參數在用戶空間地址的指針。
  • 給用戶空間的返回值也經過寄存器傳遞。在x86系統上,它存放在eax寄存器中。

5.5 系統調用的實現

5.5.1 實現系統調用

  1. 第一步,明確系統調用的用途
  2. 新系統調用的參數、返回值和錯誤碼都應該清晰;接口也要力求簡潔、參數儘量少。
  3. 系統調用設計得越通用越好。
  4. 提供機制而不是策略。
  5. 時刻注意可移植性和健壯性。

5.5.2 參數驗證

  1. 驗證參數是否合法有效
  2. 驗證指針是否有效指針

    • 指向的區域屬於用戶空間;

    • 指向的區域在進程的地址空間中(不容許訪問其餘進程空間);

    • 進程不能繞過內存訪問限制。

  3. 驗證方法:

    • 使用內核提供的copytouser()以及copyfromuser()檢查從用戶空間拷貝或者向其中寫入數據是否成功(可能引發阻塞;好比當發生缺頁中斷的時候)
    • 使用capale()函數檢查函數是否有權對指定的資源進行操做(若是不能的話則返回0)

5.6 系統調用上下文

  1. 內核在執行系統調用時處於進程上下文。current指針指向當前任務,即引起系統調用的那個進程。
  2. 在進程上下文中,內核能夠休眠而且能夠被搶佔。
  3. 當系統調用返回時,控制權仍然在system_call()中,它最終會負責切換到用戶空間,並讓用戶進程繼續執行下去。

5.6.1 綁定一個系統調用的最後步驟

  1. 在系統調用表的最後加入一個表項。
  2. 對於所支持的各類體系結構,系統調用號都必須定義於<asm/unistd.h>中。
  3. 系統調用必須被編譯進內核映像(不能被編譯成模塊)。放入kernel/下的一個相關文件中便可。
  4. 系統調用形式:

    • asmlinkage long sys_getpid(void)//以getpid()函數爲例
      • 【asmlinkage限定詞是一個編譯指令,通知編譯器僅從棧中提取該函數的參數】

5.6.2 從用戶空間訪問系統調用

  1. Linux自己提供了一組宏,用於直接對系統調用進行訪問,即_syscalln();其中n是傳遞給系統調用的參數個數
  2. 對每個宏而言,都有2+2*n個參數:第一個是系統調用返回值類型;第二個是系統調用名稱;之後是每一個參數的類型和名稱
  3. 系統調用靠C庫支持,用戶程序經過包含標準頭文件並和C庫連接,就可使用系統調用。

5.6.3 爲何不經過系統調用的方式實現

  1. 創建一個新的系統調用的好處:

    • 系統調用建立容易且使用方便
    • linux系統調用的高性能顯而易見。
  2. 問題是:

    • 你須要一個系統調用號,而這須要一個內核在處於開發版本的時候由官方分配給你。
    • 系統調用被加入穩定內核後就被固化了,爲了不應用程序的崩潰,它的接口不容許作運動。
    • 須要將系統調用分別註冊到每一個須要支持的體系結構中去。
    • 在腳本中不容易調用系統調用,也不能從文件系統直接訪問系統調用。
    • 若是僅僅進行簡單的信息交換,系統調用就大材小用了
  3. 方法:

    • 實現一個設備節點,並對此實現read()和write()。使用ioctl()對特定的設置進行操做或者對特定的信息進行檢索。
      • 像信號量這樣的某些接口,能夠用文件描述符來表示,所以也就能夠按上述方式對其進行操做。
      • 把增長的信息做爲一個文件放在sysfs的合適位置。

小結

在本章中,學到了系統調用究竟是什麼,它們與庫函數和應用程序接口有什麼關係等等。。。最後又學習了實現系統調用的優缺點讓我瞭解的更透徹了一點。

參考資料

《linux內核設計與實現》原書第三版

相關文章
相關標籤/搜索