天天看點

系統調用(一)

版權聲明:您好,轉載請留下本人部落格的位址,謝謝 https://blog.csdn.net/hongbochen1223/article/details/46609877

(一):與核心通信

系統調用在使用者空間和硬體裝置之間添加了一個中間層。該層主要有三個作用:

​1:他為使用者空間提供了一種硬體的抽象接口
​2:系統調用保證了系統的穩定和安全。
​3:每個程序都運作在虛拟系統中,而在使用者空間和系統的其餘部分提供這樣一層公共接口,也是出于這種考慮。
           

在Linux中,系統調用是使用者空間通路核心的唯一手段。

(二):API,POSIX,C庫

一般情況下,應用程式通過在使用者空間實作的應用程式設計接口(API)而不是直接通過系統調用來程式設計。一個API定義了一組應用程式使用的程式設計接口。

下面我們看一下POSIX,API,和C庫以及系統調用之間的聯系。

(三):系統調用

首先我們先看一下一個比較簡單的系統調用的實作:

SYSCALL_DEFINE0(getpid)
{
    ​return task_tgrid_vnr(current);
}
           

注意,定義中并沒有規定他要如何實作。

其中SYSCALL_DFINE0隻是一個宏,他定義一個無參數的系統調用,尾展開後的代碼為:

asmlinkage long sys_getpid(void)           

那麼我們來看一下如何定義系統調用:

首先,注意函數聲明中的asmlinkage限定詞,這是一個編譯指令,通知編譯器僅僅從棧中提取該函數的參數。所有的系統調用都需要這個限定詞。

其次,函數傳回long。為了保證32位和64位系統的相容,系統調用在使用者空間和核心空間有不同的傳回值類型,在使用者空間為int,在核心空間為long。

最後,注意系統調用get_pid()在核心中被定義成sys_getpid()。這是一個命名規則。

1:系統調用号

在Linux中,每一個系統調用被賦予一個系統調用号。這樣的話,每一個系統調用都會關聯一個系統調用。

系統調用号非常重要,一旦配置設定就不能再有任何改變,否則編譯好的應用程式就會崩潰。此外,如果一個系統調用被删除,他所占用的系統調用号也不允許被回收利用,否則,以前編譯過的代碼會調用這個系統調用,但事實上卻調用的是另外一個系統調用。在Linux中有一個”未實作“的系統調用sys_ni_syscall(),他除了傳回-ENOSYS之外不做任何事情,這個系統調用就是專門針對無效的系統調用而設的。如果一個系統調用被删除或者是變為不可用,這個系統調用就負責”填空補缺“。

在sys_call_table中,是核心記錄的所有的已注冊過的系統調用的清單。在x84-64中,定義在檔案arch/i386/kernel/syscall_64.c中。這個表為沒一個有效的系統調用指定了唯一的系統調用号。

現在我們來看一下:

const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = {
    /*
    *Smells like a like a compiler bug -- it doesn't work
    *when the & below is removed.
    *
    * 看上去像是一個編譯器bug -- 當下面的&移除之後,他就不能工作了
    */
    [0 ... __NR_syscall_max] = &sys_ni_syscall,
#include <asm/unistd_64.h>
};
           

2:系統調用的性能

Linux系統調用比其他作業系統執行的要快。Linux很短的上下文切換時間是一個重要的原因,進出核心都被優化的簡介高效。同時,系統調用處理函數和每個系統調用本身也都非常簡潔。

(四):系統調用處理函數

由于使用者空間的程式是無法執行核心程式的,所有需要一個機制來通知核心執行某個系統調用。

通知核心的機制是通過軟中斷來實作的:通過引發一個異常來促使系統切換到核心态去執行異常處理程式。此時的一場處理程式就是系統調用處理程式。有關于中斷,會在後面詳細學習。

1:指定恰當的系統調用

由于所有的系統調用陷入核心的方式都是一樣的,是以,需要将系統調用号一并傳給核心。在x86上,系統調用号是通過eax寄存器來傳遞給核心的。在陷入核心之前,使用者空間就把相應的系統調用所對應的号傳入eax中。

system_call()函數通過将給定的系統調用号與NR_syscalls做比較來檢查其有效性。如果大于或等于NR_syscalls,該函數就傳回-ENOSYS。否則,就執行相應的系統調用:

call *sys_call_table(,%rax,8)           

由于系統調用表中的表項是以63位類型存放的,是以核心需要将給定的系統調用号乘以4,然後用所得的結果在該表中查詢位置。

2:參數傳遞

同系統調用号一樣,進行參數傳遞的時候,也可以通過寄存器将參數傳遞到核心中。在x86-32系統上,ebx,ecx,edx,esi,edi按照順序存放前5個參數,此外,應該用一個單獨的寄存器存放指向所有這些參數在使用者空間位址的指針。

下面我們看一下系統調用的過程:

給使用者空間的傳回值也是通過寄存器傳遞的。在x86系統上,他存放在eax寄存器上。

繼續閱讀