歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux命令

使用 Linux 系統調用的內核命令

Linux命令

級別: 中級

M. Tim Jones ([email protected]), 顧問工程師, Emulex

2007 年 4 月 17 日

     Linux® 系統調用 —— 我們每天都在使用它們。不過您清楚系統調用是如何在用戶空間和內核之間執行的嗎?本文將探究 Linux 系統調用接口(SCI),學習如何添加新的系統調用(以及實現這種功能的其他方法),並介紹與 SCI 有關的一些工具。

    系統調用就是用戶空間應用程序和內核提供的服務之間的一個接口。由於服務是在內核中提供的,因此無法執行直接調用;相反,您必須使用一個進程來跨越用戶空間與內核之間的界限。在特定架構中實現此功能的方法會有所不同。因此,本文將著眼於最通用的架構 —— i386。

    在本文中,我將探究 Linux SCI,演示如何向 2.6.20 內核添加一個系統調用,然後從用戶空間來使用這個函數。我們還將研究在進行系統調用開發時非常有用的一些函數,以及系統調用的其他選擇。最後,我們將介紹與系統調用有關的一些輔助機制,比如在某個進程中跟蹤系統調用的使用情況。

SCI

    Linux 中系統調用的實現會根據不同的架構而有所變化,而且即使在某種給定的體架構上也會不同。例如,早期的 x86 處理器使用了中斷機制從用戶空間遷移到內核空間中,不過新的 IA-32 處理器則提供了一些指令對這種轉換進行優化(使用 sysentersysexit 指令)。由於存在大量的方法,最終結果也非常復雜,因此本文將著重於接口細節的表層討論上。

    要對 Linux 的 SCI 進行改進,您不需要完全理解 SCI 的內部原理,因此我將使用一個簡單的系統調用進程(請參看圖 1)。每個系統調用都是通過一個單一的入口點多路傳入內核。eax 寄存器用來標識應當調用的某個系統調用,這在 C 庫中做了指定(來自用戶空間應用程序的每個調用)。當加載了系統的 C 庫調用索引和參數時,就會調用一個軟件中斷(0x80 中斷),它將執行 system_call 函數(通過中斷處理程序),這個函數會按照 eax 內容中的標識處理所有的系統調用。在經過幾個簡單測試之後,使用 system_call_table 和 eax 中包含的索引來執行真正的系統調用了。從系統調用中返回後,最終執行 syscall_exit,並調用 resume_userspace 返回用戶空間。然後繼續在 C 庫中執行,它將返回到用戶應用程序中。


圖 1. 使用中斷方法的系統調用的簡化流程
系統調用的簡化流程

     SCI 的核心是系統調用多路分解表。這個表如圖 2 所示,使用 eax 中提供的索引來確定要調用該表中的哪個系統調用(sys_call_table)。圖中還給出了表內容的一些樣例,以及這些內容的位置。(有關多路分解的更多內容,請參看側欄 “系統調用多路分解”)


圖 2. 系統調用表和各種鏈接
系統調用表和各種鏈接 

添加一個 Linux 系統調用

系統調用多路分解

有些系統調用會由內核進一步進行多路分解。例如,BSD(Berkeley Software Distribution)socket 調用(socketbindconnect 等)都與一個單獨的系統調用索引(__NR_socketcall)關聯在一起,不過在內核中會進行多路分解,通過另外一個參數進入適當的調用。請參看 ./linux/net/socket.c 中的 sys_socketcall 函數。

    添加一個新系統調用主要是一些程序性的操作,但應該注意幾件事情。本節將介紹幾個系統調用的構造,從而展示它們的實現和用戶空間應用程序對它們的使用。

向內核中添加新系統調用,需要執行 3 個基本步驟:

  1. 添加新函數。
  2. 更新頭文件。
  3. 針對這個新函數更新系統調用表。

注意: 這個過程忽略了用戶空間的需求,我將稍後介紹。

    最常見的情況是,您會為自己的函數創建一個新文件。不過,為了簡單起見,我將自己的新函數添加到現有的源文件中。清單 1 所示的前兩個函數,是系統調用的簡單示例。清單 2 提供了一個使用指針參數的稍微復雜的函數。


清單 1. 系統調用示例的簡單內核函數

                
asmlinkage long sys_getjiffies( void )
{
  return (long)get_jiffies_64();
}

asmlinkage long sys_diffjiffies( long ujiffies )
{
  return (long)get_jiffies_64() - ujiffies;
}

 

Copyright © Linux教程網 All Rights Reserved