欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
使用 Linux 系統調用的內核命令

2007 年 4 月 17 日

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

系統調用就是用戶(hù)空間應用程序和內核提供的服務(wù)之間的一個(gè)接口。由于服務(wù)是在內核中提供的,因此無(wú)法執行直接調用;相反,您必須使用一個(gè)進(jìn)程來(lái)跨越用戶(hù)空間與內核之間的界限。在特定架構中實(shí)現此功能的方法會(huì )有所不同。因此,本文將著(zhù)眼于最通用的架構 —— i386。

在 本文中,我將探究 Linux SCI,演示如何向 2.6.20 內核添加一個(gè)系統調用,然后從用戶(hù)空間來(lái)使用這個(gè)函數。我們還將研究在進(jìn)行系統調用開(kāi)發(fā)時(shí)非常有用的一些函數,以及系統調用的其他選擇。最后,我們將介紹 與系統調用有關(guān)的一些輔助機制,比如在某個(gè)進(jìn)程中跟蹤系統調用的使用情況。

SCI

Linux 中系統調用的實(shí)現會(huì )根據不同的架構而有所變化,而且即使在某種給定的體架構上也會(huì )不同。例如,早期的 x86 處理器使用了中斷機制從用戶(hù)空間遷移到內核空間中,不過(guò)新的 IA-32 處理器則提供了一些指令對這種轉換進(jìn)行優(yōu)化(使用 sysentersysexit 指令)。由于存在大量的方法,最終結果也非常復雜,因此本文將著(zhù)重于接口細節的表層討論上。更詳盡的內容請參看本文最后的 參考資料。

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


圖 1. 使用中斷方法的系統調用的簡(jiǎn)化流程

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


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





回頁(yè)首


添加一個(gè) Linux 系統調用

系統調用多路分解

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

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

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

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

注意: 這個(gè)過(guò)程忽略了用戶(hù)空間的需求,我將稍后介紹。

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


清單 1. 系統調用示例的簡(jiǎn)單內核函數
                    asmlinkage long sys_getjiffies( void )                    {                    return (long)get_jiffies_64();                    }                    asmlinkage long sys_diffjiffies( long ujiffies )                    {                    return (long)get_jiffies_64() - ujiffies;                    }                    

在清單 1 中,我們?yōu)檫M(jìn)行 jiffies 監視提供了兩個(gè)函數。(有關(guān) jiffies 的更多信息,請參看側欄 “Kernel jiffies”)。第一個(gè)函數會(huì )返回當前 jiffy,而第二個(gè)函數則返回當前值與所傳遞進(jìn)來(lái)的值之間的差值。注意 asmlinkage 修飾符的使用。這個(gè)宏(在 linux/include/asm-i386/linkage.h 中定義)告訴編譯器將傳遞棧中的所有函數參數。


清單 2. 系統調用示例的最后內核函數
                    asmlinkage long sys_pdiffjiffies( long ujiffies,                    long __user *presult )                    {                    long cur_jiffies = (long)get_jiffies_64();                    long result;                    int  err = 0;                    if (presult) {                    result = cur_jiffies - ujiffies;                    err = put_user( result, presult );                    }                    return err ? -EFAULT : 0;                    }                    

內核 jiffies

Linux 內核具有一個(gè)名為 jiffies 的全局變量,它代表從機器啟動(dòng)時(shí)算起的時(shí)間滴答數。這個(gè)變量最初被初始化為 0,每次時(shí)鐘中斷時(shí)都會(huì )加 1。您可以使用 get_jiffies_64 函數來(lái)讀取 jiffies 的值,然后使用 jiffies_to_msecs 將其換算成毫秒或使用 jiffies_to_usecs 將其換算成微秒。jiffies 的全局定義和相關(guān)函數是在 ./linux/include/linux/jiffies.h 中提供的。

清單 2 給出了第三個(gè)函數。這個(gè)函數使用了兩個(gè)參數:一個(gè) long 類(lèi)型,以及一個(gè)指向被定義為 __userlong 的指針。__user 宏簡(jiǎn)單告訴編譯器(通過(guò) noderef)不應該解除這個(gè)指針的引用(因為在當前地址空間中它是沒(méi)有意義的)。這個(gè)函數會(huì )計算這兩個(gè) jiffies 值之間的差值,然后通過(guò)一個(gè)用戶(hù)空間指針將結果提供給用戶(hù)。put_user 函數將結果值放入 presult 所指定的用戶(hù)空間位置。如果在這個(gè)操作過(guò)程中出現錯誤,將立即返回,您也可以通知用戶(hù)空間調用者。

對于步驟 2 來(lái)說(shuō),我對頭文件進(jìn)行了更新:在系統調用表中為這幾個(gè)新函數安排空間。對于本例來(lái)說(shuō),我使用新系統調用號更新了 linux/include/asm/unistd.h 頭文件。更新如清單 3 中的黑體所示。


清單 3. 更新 unistd.h 文件為新系統調用安排空間
                    #define __NR_getcpu		318                    #define __NR_epoll_pwait	319                    #define __NR_getjiffies		320                    #define __NR_diffjiffies	321                    #define __NR_pdiffjiffies	322                    #define NR_syscalls	323                    

現 在已經(jīng)有了自己的內核系統調用,以及表示這些系統調用的編號。接下來(lái)需要做的是要在這些編號(表索引)和函數本身之間建立一種對等關(guān)系。這就是第 3 個(gè)步驟,更新系統調用表。如清單 4 所示,我將為這個(gè)新函數更新 linux/arch/i386/kernel/syscall_table.S 文件,它會(huì )填充清單 3 顯示的特定索引。


清單 4. 使用新函數更新系統調用表
                    .long sys_getcpu                    .long sys_epoll_pwait                    .long sys_getjiffies		/* 320 */                    .long sys_diffjiffies                    .long sys_pdiffjiffies                    

注意: 這個(gè)表的大小是由符號常量 NR_syscalls 定義的。

現在,我們已經(jīng)完成了對內核的更新。接下來(lái)必須對內核重新進(jìn)行編譯,并在測試用戶(hù)空間應用程序之前使引導使用的新映像變?yōu)榭捎谩?/p>

對用戶(hù)內存進(jìn)行讀寫(xiě)

Linux 內核提供了幾個(gè)函數,可以用來(lái)將系統調用參數移動(dòng)到用戶(hù)空間中,或從中移出。方法包括一些基本類(lèi)型的簡(jiǎn)單函數(例如 get_userput_user)。要移動(dòng)一塊兒數據(如結構或數組),您可以使用另外一組函數: copy_from_usercopy_to_user??梢允褂脤?zhuān)門(mén)的調用移動(dòng)以 null 結尾的字符串: strncpy_from_userstrlen_from_user。您也可以通過(guò)調用 access_ok 來(lái)測試用戶(hù)空間指針是否有效。這些函數都是在 linux/include/asm/uaccess.h 中定義的。

您可以使用 access_ok 宏來(lái)驗證給定操作的用戶(hù)空間指針。這個(gè)函數有 3 個(gè)參數,分別是訪(fǎng)問(wèn)類(lèi)型(VERIFY_READVERIFY_WRITE),指向用戶(hù)空間內存塊的指針,以及塊的大?。▎挝粸樽止潱?。如果成功,這個(gè)函數就返回 0:

int access_ok( type, address, size );                    

要在內核和用戶(hù)空間移動(dòng)一些簡(jiǎn)單類(lèi)型(例如 int 或 long 類(lèi)型),可以使用 get_userput_user 輕松地實(shí)現。這兩個(gè)宏都包含一個(gè)值以及一個(gè)指向變量的指針。get_user 函數將用戶(hù)空間地址(ptr)指定的值移動(dòng)到所指定的內核變量(var)中。 put_user 函數則將內核變量(var)指定的值移動(dòng)到用戶(hù)空間地址(ptr)。 如果成功,這兩個(gè)函數都返回 0:

int get_user( var, ptr );                    int put_user( var, ptr );                    

要移動(dòng)更大的對象,例如結構或數組,您可以使用 copy_from_usercopy_to_user 函數。這些函數將在用戶(hù)空間和內核之間移動(dòng)完整的數據塊。 copy_from_user 函數會(huì )將一塊數據從用戶(hù)空間移動(dòng)到內核空間,copy_to_user 則會(huì )將一塊數據從內核空間移動(dòng)到用戶(hù)空間:

unsigned long copy_from_user( void *to, const void __user *from, unsigned long n );                    unsigned long copy_to_user( void *to, const void __user *from, unsigned long n );                    

最后,您可以使用 strncpy_from_user 函數將一個(gè)以 NULL 結尾的字符串從用戶(hù)空間移動(dòng)到內核空間中。在調用這個(gè)函數之前,您可以通過(guò)調用 strlen_user 宏來(lái)獲得用戶(hù)空間字符串的大?。?/p>

long strncpy_from_user( char *dst, const char __user *src, long count );                    strlen_user( str );                    

這些函數為內核和用戶(hù)空間之間的內存移動(dòng)提供了基本功能。實(shí)際上還可以使用另外一些函數(例如減少執行檢查數量的函數)。您可以在 uaccess.h 中找到這些函數。





回頁(yè)首


使用系統調用

現在內核已經(jīng)使用新系統調用完成更新了,接下來(lái)看一下從用戶(hù)空間應用程序中使用這些系統調用需要執行的操作。使用新的內核系統調用有兩種方法。第一種方法非常方便(但是在產(chǎn)品代碼中您可能并不希望使用),第二種方法是傳統方法,需要多做一些工作。

使用第一種方法,您可以通過(guò) syscall 函數調用由其索引所標識的新函數。使用 syscall 函數,您可以通過(guò)指定它的調用索引和一組參數來(lái)調用系統調用。例如,清單 5 顯示的簡(jiǎn)單應用程序就使用其索引調用了 sys_getjiffies。


清單 5. 使用 syscall 調用系統調用
                    #include <linux/unistd.h>                    #include <sys/syscall.h>                    #define __NR_getjiffies		320                    int main()                    {                    long jiffies;                    jiffies = syscall( __NR_getjiffies );                    printf( "Current jiffies is %lx\n", jiffies );                    return 0;                    }                    

正如您所見(jiàn),syscall 函數使用了系統調用表中使用的索引作為第一個(gè)參數。如果還有其他參數需要傳遞,可以加在調用索引之后。大部分系統調用都包括了一個(gè) SYS_ 符號常量來(lái)指定自己到 __NR_ 索引的映射。例如,使用 syscall 調用 __NR_getpid 索引:

                syscall( SYS_getpid )                    

syscall 函數特定于架構,使用一種機制將控制權交給內核。其參數是基于 __NR 索引與 /usr/include/bits/syscall.h 提供的 SYS_ 符號之間的映射(在編譯 libc 時(shí)定義)。永遠都不要直接引用這個(gè)文件;而是要使用 /usr/include/sys/syscall.h 文件。

傳統的方法要求我們創(chuàng )建函數調用,這些函數調用必須匹配內核中的系統調用索引(這樣就可以調用正確的內核服務(wù)),而且參數也必須匹配。Linux 提供了一組宏來(lái)提供這種功能。_syscallN 宏是在 /usr/include/linux/unistd.h 中定義的,格式如下:

                _syscall0( ret-type, func-name )                    _syscall1( ret-type, func-name, arg1-type, arg1-name )                    _syscall2( ret-type, func-name, arg1-type, arg1-name, arg2-type, arg2-name )                    

用戶(hù)空間和 __NR 常量

注意清單 6 中提供了 __NR 符號常量。您可以在 /usr/include/asm/unistd.h 中找到它們(對于標準系統調用來(lái)說(shuō))。

_syscall 宏最多可定義 6 個(gè)參數(不過(guò)此處只顯示了 3 個(gè))。

現在,讓我們來(lái)看一下如何使用 _syscall 宏來(lái)使新系統調用對于用戶(hù)空間可見(jiàn)。清單 6 顯示的應用程序使用了 _syscall 宏定義的所有系統調用。


清單 6. 將 _syscall 宏 用于用戶(hù)空間應用程序開(kāi)發(fā)
                    #include <stdio.h>                    #include <linux/unistd.h>                    #include <sys/syscall.h>                    #define __NR_getjiffies		320                    #define __NR_diffjiffies	321                    #define __NR_pdiffjiffies	322                    _syscall0( long, getjiffies );                    _syscall1( long, diffjiffies, long, ujiffies );                    _syscall2( long, pdiffjiffies, long, ujiffies, long*, presult );                    int main()                    {                    long jifs, result;                    int err;                    jifs = getjiffies();                    printf( "difference is %lx\n", diffjiffies(jifs) );                    err = pdiffjiffies( jifs, &result );                    if (!err) {                    printf( "difference is %lx\n", result );                    } else {                    printf( "error\n" );                    }                    return 0;                    }                    

注意 __NR 索引在這個(gè)應用程序中是必需的,因為 _syscall 宏使用了 func-name 來(lái)構造 __NR 索引(getjiffies -> __NR_getjiffies)。其結果是您可以使用它們的名字來(lái)調用內核函數,就像其他任何系統調用一樣。





回頁(yè)首


用戶(hù)/內核交互的其他選擇

系 統調用是請求內核中服務(wù)的一種有效方法。使用這種方法的最大問(wèn)題就是它是一個(gè)標準接口,很難將新的系統調用增加到內核中,因此可以通過(guò)其他方法來(lái)實(shí)現類(lèi)似 服務(wù)。如果您無(wú)意將自己的系統調用加入公共的 Linux 內核中,那么系統調用就是將內核服務(wù)提供給用戶(hù)空間的一種方便而且有效的方法。

讓您的服務(wù)對用戶(hù)空間可見(jiàn)的另外一種方法是通過(guò) /proc 文件系統。/proc 文件系統是一個(gè)虛擬文件系統,您可以通過(guò)它來(lái)向用戶(hù)提供一個(gè)目錄和文件,然后通過(guò)文件系統接口(讀、寫(xiě)等)在內核中為新服務(wù)提供一個(gè)接口。





回頁(yè)首


使用 strace 跟蹤系統調用

Linux 內核提供了一種非常有用的方法來(lái)跟蹤某個(gè)進(jìn)程所調用的系統調用(以及該進(jìn)程所接收到的信號)。這個(gè)工具就是 strace,它可以在命令行中執行,使用希望跟蹤的應用程序作為參數。例如,如果您希望了解在執行 date 命令時(shí)都執行了哪些系統調用,可以鍵入下面的命令:

strace date                    

結果會(huì )產(chǎn)生大量信息,顯示在執行 date 命令過(guò)程中所執行的各個(gè)系統調用。您會(huì )看到加載共享庫、映射內存,最后跟蹤到的是在標準輸出中生成日期信息:

...                    write(1, "Fri Feb  9 23:06:41 MST 2007\n", 29Fri Feb  9 23:06:41 MST 2007) = 29                    munmap(0xb747a000, 4096)	= 0                    exit_group(0)			= ?                    $                    

當當前系統調用請求具有一個(gè)名為 syscall_trace 的特定字段集(它導致 do_syscall_trace 函數的調用)時(shí),將在內核中完成跟蹤。您還可以看到跟蹤調用是 ./linux/arch/i386/kernel/entry.S 中系統調用請求的一部分(請參看 syscall_trace_entry)。





回頁(yè)首


結束語(yǔ)

分享這篇文章……

將本文提交到 Digg
發(fā)布到 del.icio.us
提交到 Slashdot!

系統調用是穿越用戶(hù)空間和內核空間,請求內核空間服務(wù)的一種有效方法。不過(guò)對這種方法的控制也很?chē)栏?,更?jiǎn)單的方式是增加一個(gè)新的 /proc 文件系統項來(lái)提供用戶(hù)/內核間的交互。不過(guò)當速度因素非常重要時(shí),系統調用則是使應用程序獲得最佳性能的理想方法。請參看 參考資料 的內容進(jìn)一步了解 SCI。



參考資料

學(xué)習


關(guān)于作者

M. Tim Jones 是一名嵌入式軟件工程師,他是 GNU/Linux Application Programming、AI Application Programming 以及 BSD Sockets Programming from a Multilanguage Perspective 等書(shū)的作者。他的工程背景非常廣泛,從同步宇宙飛船的內核開(kāi)發(fā)到嵌入式架構設計,再到網(wǎng)絡(luò )協(xié)議的開(kāi)發(fā)。Tim 是位于科羅拉多州 Longmont 的 Emulex Corp. 的一名顧問(wèn)工程師。

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
linux內核分析筆記----系統調用
用戶(hù)空間和內核空間通訊之【系統調用】
linux系統調用機制分析
Linux內核編程實(shí)戰經(jīng)驗談 - [藍森林-自由軟件]
linux系統調用
linux 系統調用
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久