《Linux內核設計與實現》讀書筆記——第五章

《Linux內核設計與實現》讀書筆記——第五章

標籤(空格分隔): 20135321餘佳源linux


第五章 系統調用

操做系統中,內核提供了用戶進程與內核進行交互的一組接口。這些接口讓應用程序受限的訪問硬件設備,提供了新進程與已有進程進行通訊的機制,也提供了申請操做系統其餘資源的能力。提供接口主要是爲了保證系統穩定可靠,避免應用程序恣意妄行。編程

  • 使應用程序受限地訪問硬件設備
  • 提供建立新進程與已有進程進行通訊的機制
  • 提供申請操做系統其餘資源的能力

知識點回顧:
系統調用:須要int 0x80模擬中斷讓硬件觸發。同步、主動地進入系統空間。
硬中斷:異步、被動地進入系統空間。CPU運行時發生錯誤則中斷,中斷後沒有進程調度。
軟中斷:中斷後還執行其餘進程調度。
系統調用過程:切換執行路徑&切換運行級別
系統調用表:定義於arch/i386/kernel/syscall_table.S ,映射系統調用號和與系統調用。
系統調用的過程:int 0x80 -> 中斷向量表 ->系統調用表 JUMP(EAX*4+基地址)根據系統調用號找到對應的系統調用代碼並執行。api

1、與內核通訊

系統調用在用戶控件進程和硬件設備之間添加了一箇中間層,做用以下:安全

·爲用戶空間提供了一種硬件的抽象接口
·系統調用保證了系統的穩定和安全
·每一個進程都運行在虛擬系統中,而在用戶控件和系統的其他部分提供這樣一層公共接口

在Linux中,系統調用是用戶控件訪問內核的惟一手段;除異常和陷入外,他們是內核惟一的合法入口。異步

系統調用與API的不一樣在於運行級別的切換。函數

2、API、POSIX和C庫

應用程序經過在用戶空間實現的應用編程接口(API)而非直接經過系統調用來編程。性能

POSIX是應用編程接口的一個國際標準,C庫提供了POSIX的絕大部分API。操作系統

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

關於Unix接口設計:提供機制而不是策略

POSIX、API、C庫和系統調用之間的關係以下圖:
設計

3、系統調用

  • 系統調用也就是 --> syscall 一般經過C庫中定義的函數調用來進行系統調用。返回值是long型變量,若是出錯,C庫會將錯誤代碼寫入errno全局變量。
  • 系統調用再出現錯誤的時候C庫會把錯誤碼寫入errno全局變量。經過調用perror()庫函數,能夠把該變量翻譯成用戶能夠理解的錯誤字符串。
  • SYSCALL_DEFINE0只是一個宏,它定義一個無參數的系統調用。
  • asmlinkage——這是一個編譯指令,通知編譯器僅從棧中提取該函數的參數。

    全部的系統調用都須要這個限定詞
    其次函數返回long

  • 系統調用在用戶空間和內核空間有不一樣的返回值類型,在用戶空間爲int在內核空間爲long。

定義系統調用:要訪問系統調用,一般經過C庫定義的函數調用來進行

例:asmlinkage long sys_getpid(void)

  1. asmlingkage,這是一個編譯指令,通知編譯器僅從棧中提取該函數的參數。全部的系統調用都須要這個限定詞。
  2. 返回值long。
    爲了保證32位和64位系統的兼容,系統調用在用戶空間返回值int,內核空間long
  3. 命名規則:sys_xxx

1.系統調用號

  1. 在Linux系統中每一個系統調用被賦予一個系統調用號,當用戶空間的進程執行一個系統調用時,系統調用號用來指明執行哪一個系統調用
  2. 系統調用號一旦分配就不會再更改,被刪除的系統調用號也不準再回收。
  3. sys _ ni _ syscall()專門針對無效的系統調用而設立的,只負責返回-ENOSYS
  4. 系統調用號被定義在arch/i386/kernel/syscall_64.c文件中

2.系統調用的性能——執行速度快

緣由:

  • 很短的上下文切換時間
  • 系統調用處理程序和每一個系統調用自己也十分簡潔

4、系統調用處理程序

由於用戶控件不能直接執行內核代碼,須要切換內核態,它須要用某種方式告知內核,本身須要執行一個系統調用,請求切換到內核態,這個通知內核的機制是軟中斷

經過引起一個異常來促使系統切換到內核態去執行異常處理程序,此時的異常處理程序實際上即系統調用處理程序system_call(),中斷號128,指令以下:
int 128
或者
int 0x80
退出是iret

它與硬件體系結構緊密相關

新指令sysenter,比int中斷指令更快更專業。

1.指定恰當的系統調用

  1. 在x86上,系統調用號是經過eax寄存器傳遞給內核的。
  2. system_call()函數經過將給定的系統調用號與NR——syscall作比較來檢查其有效性。
  3. 因爲系統調用表中的表項是以64位類型存放的,內核須要將給定的系統調用號乘以4,而後用所獲得的結果在該表中查詢其位置。

2.參數調用

參數傳遞最簡單的辦法是像傳遞系統調用號同樣,把這些參數也放在寄存器裏。在X86-32系統上,ebx、ecx、edx、esi、edi按順序存放前五個參數。須要六個或六個以上參數時應用一個單獨的寄存器存放指向全部這些參數在用戶空間地址的指針。

system_call()函數經過將給定的系統調用號與NR_syscalls做比較來檢查其有效性。大於等於返回-ENOSYS,不然執行相應系統調用:

call *sys_call_table(,%rax,8)

過程:
int 0x80 → 中斷向量表中找到對應中斷向量 →系統調用表

JMP(EAX*4 + system_xxx),乘以4是由於系統調用表中的表象是以64位存放的。

5、系統調用的實現

1. 決定系統調用的用途

  • 明確新系統調用的參數、返回值和錯誤碼,系統調用的接口力求簡潔
  • 不少系統調用提供了標誌參數以確保向前兼容。標誌並非用來讓單個系統調用具備多個不一樣的行別行爲,爲了即便增長新的功能和選項,也不破壞向後兼容或不須要增長新的系統調用
  • 確保系統調用的可移植性和健壯性

2. 參數驗證

  • 與文件I/O相關的系統調用必須檢查文件描述符是否有效
  • 與進程相關的函數必須檢查提供的PID是否有效
  • 最重要的一種檢查就是檢查用戶提供的指針是否有效。內核必須保證:

    指針指向的內存區域屬於用戶空間
    指針指向的內存區域在進程的地址空間裏,進程決不能繞過內存訪問限制
    進程不能繞過內存訪問限制

  • 最後一項檢查針對是否有合法權限

3. 系統調用上下文

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

4. 綁定系統調用

  • 首先在系統調用表的最後加入一個表項
  • 對於所支持的各類體系結構,系統調用號都必須定義於<asm/unistd.h>中(系統調用號是專屬於體系結構ABI(應用程序二進制接口)的部分)
  • 系統調用必須被編譯進內核映象(不能被編譯成模塊)。好比sys.c,它包含了各類各樣的系統調用

5. 從用戶空間訪問系統調用

  • 用戶程序經過包含標準頭文件並和C庫連接,就可使用系統調用
  • 對於每一個宏來講,都有2+2*n個參數

    第一個參數對應着系統調用的返回值類型
    第二個參數是系統調用的名稱
    在之後是按照系統調用參數的順序排列的每一個參數的類型和名稱

6、系統調用上下文

內核在執行系統調用時處於進程上下文。
在進程上下文中,內核能夠:
休眠說明系統調用可使用內核提供的絕大部分功能
能夠被搶佔要求保證該系統調用是可重入的

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

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

2.從用戶空間訪問系統調用

Linux自己提供了一組宏,用於直接對系統調用進行訪問。

_syscalln()     //n的範圍從0到6,表明須要傳遞給系統調用的參數個數。

例如:

long open(const char *filename, int flags, int mode)=#define NR_open 5_syscall3(long, open,const char*, filename, int, flags, int, mode)
應用程序能夠直接用open()了。

對於每一個宏來講,都有(2+2xn)個參數:

1.系統調用的返回值類型
2.系統調用的名稱
之後按照系統調用參數的順序排列每一個參數的類型和名稱。

_NR_open在<asm/unistd.h>中定義。
這個宏會被擴展成爲內嵌彙編的C函數。

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

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

  • 系統調用建立容易且使用方便
  • Linux系統調用的高性能顯而易見

問題是:

  • 你須要―個系統調用號,而這須要一個內核在處於開發版本的時候由官方分配給你
  • 系統調用被加入穩定內核後就被固化了,它的接口不容許作改動
  • 須要將系統調用分別註冊到每一個須要支持的體系結構中去
  • 在腳本中不容易調用系統調用,也不能從文件系統直接訪問系統調用
  • 因爲你須要系統調用號,所以在主內核樹以外是很難維護和使用系統調用的

替代方法:
實現一個設備節點,並對此實現read()和write()。使用特定的信息進行檢索。

  • 像信號量這樣的某些接口,能夠用文件描述符來表示,所以也就能夠按上述方式對其進行操做
  • 把增長的信息做爲一個文件放在sysfs的合適位置

7、總結

Linux儘可能避免每出現一種新的抽象就簡單地加入一個新的系統調用。

系統調用究竟是什麼?

  • 系統調用是操做系統爲用戶態進程與硬件設備進行交互提供的一組接口,讓應用程序受限的訪問硬件設備,提供了新進程與已有進程進行通訊的機制,也提供了申請操做系統其餘資源的能力。能夠保證系統穩定可靠。

執行系統調用的連鎖反應有?

  • 陷入內核
  • 傳遞系統調用號和參數
  • 執行正確的系統調用函數
  • 並把返回值帶回用戶空間。
相關文章
相關標籤/搜索