那曲檬骨新材料有限公司

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Linux系統(tǒng)調用概述

科技綠洲 ? 來源:Linux開發(fā)架構之路 ? 作者:Linux開發(fā)架構之路 ? 2023-11-09 10:27 ? 次閱讀

系統(tǒng)調用概述

計算機系統(tǒng)的各種硬件資源是有限的,在現(xiàn)代多任務操作系統(tǒng)上同時運行的多個進程都需要訪問這些資源,為了更好的管理這些資源進程是不允許直接操作的,所有對這些資源的訪問都必須有操作系統(tǒng)控制。也就是說操作系統(tǒng)是使用這些資源的唯一入口,而這個入口就是操作系統(tǒng)提供的系統(tǒng)調用(System Call)。在linux中系統(tǒng)調用是用戶空間訪問內核的唯一手段,除異常和陷入外,他們是內核唯一的合法入口。

一般情況下應用程序通過應用編程接口API,而不是直接通過系統(tǒng)調用來編程。在Unix世界,最流行的API是基于POSIX標準的。

操作系統(tǒng)一般是通過中斷從用戶態(tài)切換到內核態(tài)。中斷就是一個硬件或軟件請求,要求CPU暫停當前的工作,去處理更重要的事情。比如,在x86機器上可以通過int指令進行軟件中斷,而在磁盤完成讀寫操作后會向CPU發(fā)起硬件中斷。

中斷有兩個重要的屬性,中斷號和中斷處理程序。中斷號用來標識不同的中斷,不同的中斷具有不同的中斷處理程序。在操作系統(tǒng)內核中維護著一個中斷向量表(Interrupt Vector Table),這個數(shù)組存儲了所有中斷處理程序的地址,而中斷號就是相應中斷在中斷向量表中的偏移量。

一般地,系統(tǒng)調用都是通過軟件中斷實現(xiàn)的,x86系統(tǒng)上的軟件中斷由int $0x80指令產(chǎn)生,而128號異常處理程序就是系統(tǒng)調用處理程序system_call(),它與硬件體系有關,在entry.S中用匯編寫。接下來就來看一下Linux下系統(tǒng)調用具體的實現(xiàn)過程。

為什么需要系統(tǒng)調用

linux內核中設置了一組用于實現(xiàn)系統(tǒng)功能的子程序,稱為系統(tǒng)調用。系統(tǒng)調用和普通庫函數(shù)調用非常相似,只是系統(tǒng)調用由操作系統(tǒng)核心提供,運行于內核態(tài),而普通的函數(shù)調用由函數(shù)庫或用戶自己提供,運行于用戶態(tài)。

一般的,進程是不能訪問內核的。它不能訪問內核所占內存空間也不能調用內核函數(shù)。CPU硬件決定了這些(這就是為什么它被稱作“保護模式”)。

為了和用戶空間上運行的進程進行交互,內核提供了一組接口。透過該接口,應用程序可以訪問硬件設備和其他操作系統(tǒng)資源。這組接口在應用程序和內核之間扮演了使者的角色,應用程序發(fā)送各種請求,而內核負責滿足這些請求(或者讓應用程序暫時擱置)。實際上提供這組接口主要是為了保證系統(tǒng)穩(wěn)定可靠,避免應用程序肆意妄行,惹出大麻煩。

系統(tǒng)調用在用戶空間進程和硬件設備之間添加了一個中間層。該層主要作用有三個:

1、它為用戶空間提供了一種統(tǒng)一的硬件的抽象接口。比如當需要讀些文件的時候,應用程序就可以不去管磁盤類型和介質,甚至不用去管文件所在的文件系統(tǒng)到底是哪種類型。

2、系統(tǒng)調用保證了系統(tǒng)的穩(wěn)定和安全。作為硬件設備和應用程序之間的中間人,內核可以基于權限和其他一些規(guī)則對需要進行的訪問進行裁決。舉例來說,這樣可以避免應用程序不正確地使用硬件設備,竊取其他進程的資源,或做出其他什么危害系統(tǒng)的事情。

3、每個進程都運行在虛擬系統(tǒng)中,而在用戶空間和系統(tǒng)的其余部分提供這樣一層公共接口,也是出于這種考慮。如果應用程序可以隨意訪問硬件而內核又對此一無所知的話,幾乎就沒法實現(xiàn)多任務和虛擬內存,當然也不可能實現(xiàn)良好的穩(wěn)定性和安全性。在Linux中,系統(tǒng)調用是用戶空間訪問內核的惟一手段;除異常和中斷外,它們是內核惟一的合法入口。

API/POSIX/C庫的區(qū)別與聯(lián)系

一般情況下,應用程序通過應用編程接口(API)而不是直接通過系統(tǒng)調用來編程。這點很重要,因為應用程序使用的這種編程接口實際上并不需要和內核提供的系統(tǒng)調用一一對應。

一個API定義了一組應用程序使用的編程接口。它們可以實現(xiàn)成一個系統(tǒng)調用,也可以通過調用多個系統(tǒng)調用來實現(xiàn),而完全不使用任何系統(tǒng)調用也不存在問題。實際上,API可以在各種不同的操作系統(tǒng)上實現(xiàn),給應用程序提供完全相同的接口,而它們本身在這些系統(tǒng)上的實現(xiàn)卻可能迥異。

在Unix世界中,最流行的應用編程接口是基于POSIX標準的,其目標是提供一套大體上基于Unix的可移植操作系統(tǒng)標準。POSIX是說明API和系統(tǒng)調用之間關系的一個極好例子。在大多數(shù)Unix系統(tǒng)上,根據(jù)POSIX而定義的API函數(shù)和系統(tǒng)調用之間有著直接關系。

Linux的系統(tǒng)調用像大多數(shù)Unix系統(tǒng)一樣,作為C庫的一部分提供如下圖所示。C庫實現(xiàn)了 Unix系統(tǒng)的主要API,包括標準C庫函數(shù)和系統(tǒng)調用。所有的C程序都可以使用C庫,而由于C語言本身的特點,其他語言也可以很方便地把它們封裝起來使用。

程序員的角度看,系統(tǒng)調用無關緊要,他們只需要跟API打交道就可以了。相反,內核只跟系統(tǒng)調用打交道;庫函數(shù)及應用程序是怎么使用系統(tǒng)調用不是內核所關心的。

關于Unix的界面設計有一句通用的格言“提供機制而不是策略”。換句話說,Unix的系統(tǒng)調用抽象出了用于完成某種確定目的的函數(shù)。至干這些函數(shù)怎么用完全不需要內核去關心。區(qū)別對待機制(mechanism)和策略(policy)是Unix設計中的一大亮點。大部分的編程問題都可以被切割成兩個部分:“需要提供什么功能”(機制)和“怎樣實現(xiàn)這些功能”(策略)。

區(qū)別

api是函數(shù)的定義,規(guī)定了這個函數(shù)的功能,跟內核無直接關系。而系統(tǒng)調用是通過中斷向內核發(fā)請求,實現(xiàn)內核提供的某些服務。

聯(lián)系

一個api可能會需要一個或多個系統(tǒng)調用來完成特定功能。通俗點說就是如果這個api需要跟內核打交道就需要系統(tǒng)調用,否則不需要。程序員調用的是API(API函數(shù)),然后通過與系統(tǒng)調用共同完成函數(shù)的功能。因此,API是一個提供給應用程序的接口,一組函數(shù),是與程序員進行直接交互的。系統(tǒng)調用則不與程序員進行交互的,它根據(jù)API函數(shù),通過一個軟中斷機制向內核提交請求,以獲取內核服務的接口。并不是所有的API函數(shù)都一一對應一個系統(tǒng)調用,有時,一個API函數(shù)會需要幾個系統(tǒng)調用來共同完成函數(shù)的功能,甚至還有一些API函數(shù)不需要調用相應的系統(tǒng)調用(因此它所完成的不是內核提供的服務)

系統(tǒng)調用的實現(xiàn)原理

基本機制

前文已經(jīng)提到了Linux下的系統(tǒng)調用是通過0x80實現(xiàn)的,但是我們知道操作系統(tǒng)會有多個系統(tǒng)調用(Linux下有319個系統(tǒng)調用),而對于同一個中斷號是如何處理多個不同的系統(tǒng)調用的?最簡單的方式是對于不同的系統(tǒng)調用采用不同的中斷號,但是中斷號明顯是一種稀缺資源,Linux顯然不會這么做;還有一個問題就是系統(tǒng)調用是需要提供參數(shù),并且具有返回值的,這些參數(shù)又是怎么傳遞的?也就是說,對于系統(tǒng)調用我們要搞清楚兩點:

  1. 1.系統(tǒng)調用的函數(shù)名稱轉換。
  2. 2.系統(tǒng)調用的參數(shù)傳遞。

首先看第一個問題。實際上,Linux中每個系統(tǒng)調用都有相應的系統(tǒng)調用號作為唯一的標識,內核維護一張系統(tǒng)調用表,sys_call_table,表中的元素是系統(tǒng)調用函數(shù)的起始地址,而系統(tǒng)調用號就是系統(tǒng)調用在調用表的偏移量。在x86上,系統(tǒng)調用號是通過eax寄存器傳遞給內核的。比如fork()的實現(xiàn):

用戶空間的程序無法直接執(zhí)行內核代碼。它們不能直接調用內核空間中的函數(shù),因為內核駐留在受保護的地址空間上。如果進程可以直接在內核的地址空間上讀寫的話,系統(tǒng)安全就會失去控制。所以,應用程序應該以某種方式通知系統(tǒng),告訴內核自己需要執(zhí)行一個系統(tǒng)調用,希望系統(tǒng)切換到內核態(tài),這樣內核就可以代表應用程序來執(zhí)行該系統(tǒng)調用了。

通知內核的機制是靠軟件中斷實現(xiàn)的。首先,用戶程序為系統(tǒng)調用設置參數(shù)。其中一個參數(shù)是系統(tǒng)調用編號。參數(shù)設置完成后,程序執(zhí)行“系統(tǒng)調用”指令。x86系統(tǒng)上的軟中斷由int產(chǎn)生。這個指令會導致一個異常:產(chǎn)生一個事件,這個事件會致使處理器切換到內核態(tài)并跳轉到一個新的地址,并開始執(zhí)行那里的異常處理程序。此時的異常處理程序實際上就是系統(tǒng)調用處理程序。它與硬件體系結構緊密相關。

新地址的指令會保存程序的狀態(tài),計算出應該調用哪個系統(tǒng)調用,調用內核中實現(xiàn)那個系統(tǒng)調用的函數(shù),恢復用戶程序狀態(tài),然后將控制權返還給用戶程序。系統(tǒng)調用是設備驅動程序中定義的函數(shù)最終被調用的一種方式。

從系統(tǒng)分析的角度,linux的系統(tǒng)調用涉及4個方面的問題。

響應函數(shù)sys_xxx

響應函數(shù)名以“sys_”開頭,后跟該系統(tǒng)調用的名字。

例如
系統(tǒng)調用fork()的響應函數(shù)是sys_fork()(見Kernel/fork.c),
exit()的響應函數(shù)是sys_exit()(見kernel/fork.)。

系統(tǒng)調用表與系統(tǒng)調用號-=>數(shù)組與下標

文件include/asm/unisted.h為每個系統(tǒng)調用規(guī)定了唯一的編號。

圖片

在我們系統(tǒng)中/usr/include/asm/unistd_32.h,可以通過find / -name unistd_32.h -print查找)
而內核中的頭文件路徑不同的內核版本以及不同的發(fā)行版,文件的存儲結構可能有所區(qū)別

圖片

圖片

圖片

圖片

假設用name表示系統(tǒng)調用的名稱,那么系統(tǒng)調用號與系統(tǒng)調用響應函數(shù)的關系是:以系統(tǒng)調用號_NR_name作為下標,可找出系統(tǒng)調用表sys_call_table(見arch/i386/kernel/entry.S)中對應表項的內容,它正好是該系統(tǒng)調用的響應函數(shù)sys_name的入口地址。

系統(tǒng)調用表sys_call_table記錄了各sys_name函數(shù)在表中的位置,共190項。有了這張表,就很容易根據(jù)特定系統(tǒng)調用

圖片

在表中的偏移量,找到對應的系統(tǒng)調用響應函數(shù)的入口地址。系統(tǒng)調用表共256項,余下的項是可供用戶自己添加的系統(tǒng)調用空間。

在Linux中,每個系統(tǒng)調用被賦予一個系統(tǒng)調用號。這樣,通過這個獨一無二的號就可以關聯(lián)系統(tǒng)調用。當用戶空間的進程執(zhí)行一個系統(tǒng)調用的時候,這個系統(tǒng)調用號就被用來指明到底是要執(zhí)行哪個系統(tǒng)調用。進程不會提及系統(tǒng)調用的名稱。

系統(tǒng)調用號相當關鍵,一旦分配就不能再有任何變更,否則編譯好的應用程序就會崩潰。Linux有一個“未實現(xiàn)”系統(tǒng)調用sys_ni_syscall(),它除了返回一ENOSYS外不做任何其他工作,這個錯誤號就是專門針對無效的系統(tǒng)調用而設的。

因為所有的系統(tǒng)調用陷入內核的方式都一樣,所以僅僅是陷入內核空間是不夠的。因此必須把系統(tǒng)調用號一并傳給內核。在x86上,系統(tǒng)調用號是通過eax寄存器傳遞給內核的。在陷人內核之前,用戶空間就把相應系統(tǒng)調用所對應的號放入eax中了。這樣系統(tǒng)調用處理程序一旦運行,就可以從eax中得到數(shù)據(jù)。其他體系結構上的實現(xiàn)也都類似。

內核記錄了系統(tǒng)調用表中的所有已注冊過的系統(tǒng)調用的列表,存儲在sys_call_table中。它與體系結構有關,一般在entry.s中定義。這個表中為每一個有效的系統(tǒng)調用指定了惟一的系統(tǒng)調用號。sys_call_table是一張由指向實現(xiàn)各種系統(tǒng)調用的內核函數(shù)的函數(shù)指針組成的表:system_call()函數(shù)通過將給定的系統(tǒng)調用號與NR_syscalls做比較來檢查其有效性。如果它大于或者等于NR syscalls,該函數(shù)就返回一ENOSYS。否則,就執(zhí)行相應的系統(tǒng)調用。

圖片

call *sys_ call-table(,%eax, 4)1

由于系統(tǒng)調用表中的表項是以32位(4字節(jié))類型存放的,所以內核需要將給定的系統(tǒng)調用號乘以4,然后用所得的結果在該表中查詢其位置

進程的系統(tǒng)調用命令轉換為INT 0x80中斷的過程

宏定義_syscallN()見include/asm/unisted.h)用于系統(tǒng)調用的格式轉換和參數(shù)的傳遞。N取0~5之間的整數(shù)。

參數(shù)個數(shù)為N的系統(tǒng)調用由_syscallN()負責格式轉換和參數(shù)傳遞。系統(tǒng)調用號放入EAX寄存器,啟動INT 0x80后,規(guī)定返回值送EAX寄存器。

系統(tǒng)調用功能模塊的初始化

對系統(tǒng)調用的初始化也就是對INT 0x80的初始化。

系統(tǒng)啟動時,匯編子程序setup_idt(見arch/i386/kernel/head.S)準備了1張256項的idt表,由start_kernel()(見init/main.c),trap_init()(見arch/i386/kernel/traps.c)調用的C語言宏定義set_system_gate(0x80,&system_call)(見include/asm/system.h)設置0x80號軟中斷的服務程序為 system_call(見arch/i386/kernel/entry.S), system.call就是所有系統(tǒng)調用的總入口。

內核如何為各種系統(tǒng)調用服務

當進程需要進行系統(tǒng)調用時,必須以C語言函數(shù)的形式寫一句系統(tǒng)調用命令。該命令如果已在某個頭文件中由相應的_syscallN()展開,則用戶程序必須包含該文件。當進程執(zhí)行到用戶程序的系統(tǒng)調用命令時,實際上執(zhí)行了由宏命令_syscallN()展開的函數(shù)。系統(tǒng)調用的參數(shù) 由各通用寄存器傳遞,然后執(zhí)行INT 0x80,以內核態(tài)進入入口地址system_call。

ret_from_sys_call

以ret_from_sys_call入口的匯編程序段在linux進程管理中起到了十分重要的作用。

所有系統(tǒng)調用結束前以及大部分中斷服務返回前,都會跳轉至此處入口地址。該段程序不僅僅為系統(tǒng)調用服務,它還處理中斷嵌套、CPU調度、信號等事務。

內核如何為系統(tǒng)調用的參數(shù)傳遞參數(shù)

參數(shù)傳遞

除了系統(tǒng)調用號以外,大部分系統(tǒng)調用都還需要一些外部的參數(shù)輸人。所以,在發(fā)生異常的時候,應該把這些參數(shù)從用戶空間傳給內核。最簡單的辦法就是像傳遞系統(tǒng)調用號一樣把這些參數(shù)也存放在寄存器里。在x86系統(tǒng)上,ebx, ecx, edx, esi和edi按照順序存放前五個參數(shù)。需要六個或六個以上參數(shù)的情況不多見,此時,應該用一個單獨的寄存器存放指向所有這些參數(shù)在用戶空間地址的指針。

給用戶空間的返回值也通過寄存器傳遞。在x86系統(tǒng)上,它存放在eax寄存器中。接下來許多關于系統(tǒng)調用處理程序的描述都是針對x86版本的。但不用擔心,所有體系結構的實現(xiàn)都很類似。

參數(shù)驗證

系統(tǒng)調用必須仔細檢查它們所有的參數(shù)是否合法有效。舉例來說,與文件I/O相關的系統(tǒng)調用必須檢查文件描述符是否有效。與進程相關的函數(shù)必須檢查提供的PID是否有效。必須檢查每個參數(shù),保證它們不但合法有效,而且正確。

最重要的一種檢查就是檢查用戶提供的指針是否有效。試想,如果一個進程可以給內核傳遞指針而又無須被檢查,那么它就可以給出一個它根本就沒有訪問權限的指針,哄騙內核去為它拷貝本不允許它訪問的數(shù)據(jù),如原本屬于其他進程的數(shù)據(jù)。在接收一個用戶空間的指針之前,內核必須保證:

  • 指針指向的內存區(qū)域屬于用戶空間。進程決不能哄騙內核去讀內核空間的數(shù)據(jù)。
  • 指針指向的內存區(qū)域在進程的地址空間里。進程決不能哄騙內核去讀其他進程的數(shù)據(jù)。
  • 如果是讀,該內存應被標記為可讀。如果是寫,該內存應被標記為可寫。進程決不能繞過內存訪問限制。

內核提供了兩個方法來完成必須的檢查和內核空間與用戶空間之間數(shù)據(jù)的來回拷貝。注意,內核無論何時都不能輕率地接受來自用戶空間的指針!這兩個方法中必須有一個被調用。為了向用戶空間寫入數(shù)據(jù),內核提供了copy_to_user(),它需要三個參數(shù)。第一個參數(shù)是進程空間中的目的內存地址。第二個是內核空間內的源地址。最后一個參數(shù)是需要拷貝的數(shù)據(jù)長度(字節(jié)數(shù))。

為了從用戶空間讀取數(shù)據(jù),內核提供了copy_from_ user(),它和copy-to-User()相似。該函數(shù)把第二個參數(shù)指定的位置上的數(shù)據(jù)拷貝到第一個參數(shù)指定的位置上,拷貝的數(shù)據(jù)長度由第三個參數(shù)決定。

如果執(zhí)行失敗,這兩個函數(shù)返回的都是沒能完成拷貝的數(shù)據(jù)的字節(jié)數(shù)。如果成功,返回0。當出現(xiàn)上述錯誤時,系統(tǒng)調用返回標準-EFAULT。

注意copy_to_user()和copy_from_user()都有可能引起阻塞。當包含用戶數(shù)據(jù)的頁被換出到硬盤上而不是在物理內存上的時候,這種情況就會發(fā)生。此時,進程就會休眠,直到缺頁處理程序將該頁從硬盤重新?lián)Q回物理內存。

系統(tǒng)調用的返回值

系統(tǒng)調用(在Linux中常稱作syscalls)通常通過函數(shù)進行調用。它們通常都需要定義一個或幾個參數(shù)(輸入)而且可能產(chǎn)生一些副作用,例如寫某個文件或向給定的指針拷貝數(shù)據(jù)等等。為防止和正常的返回值混淆,系統(tǒng)調用并不直接返回錯誤碼,而是將錯誤碼放入一個名為errno的全局變量中。通常用一個負的返回值來表明錯誤。返回一個0值通常表明成功。如果一個系統(tǒng)調用失敗,你可以讀出errno的值來確定問題所在。通過調用perror()庫函數(shù),可以把該變量翻譯成用戶可以理解的錯誤字符串。

errno不同數(shù)值所代表的錯誤消息定義在errno.h中,你也可以通過命令”man 3 errno”來察看它們。需要注意的是,errno的值只在函數(shù)發(fā)生錯誤時設置,如果函數(shù)不發(fā)生錯誤,errno的值就無定義,并不會被置為0。另外,在處理errno前最好先把它的值存入另一個變量,因為在錯誤處理過程中,即使像printf()這樣的函數(shù)出錯時也會改變errno的值。

當然,系統(tǒng)調用最終具有一種明確的操作。舉例來說,如getpid()系統(tǒng)調用,根據(jù)定義它會返回當前進程的PID。內核中它的實現(xiàn)非常簡單:

asmlinkage long sys_ getpid(void)
{
return current-> tgid;
}

上述的系統(tǒng)調用盡管非常簡單,但我們還是可以從中發(fā)現(xiàn)兩個特別之處。首先,注意函數(shù)聲明中的asmlinkage限定詞,這是一個小戲法,用于通知編譯器僅從棧中提取該函數(shù)的參數(shù)。所有的系統(tǒng)調用都需要這個限定詞。其次,注意系統(tǒng)調用get_pid()在內核中被定義成sys_ getpid。這是Linux中所有系統(tǒng)調用都應該遵守的命名規(guī)則。

訪問系統(tǒng)調用

系統(tǒng)調用上下文

內核在執(zhí)行系統(tǒng)調用的時候處于進程上下文。current指針指向當前任務,即引發(fā)系統(tǒng)調用的那個進程。

在進程上下文中,內核可以休眠并且可以被搶占。這兩點都很重要。首先,能夠休眠說明系統(tǒng)調用可以使用內核提供的絕大部分功能。休眠的能力會給內核編程帶來極大便利。在進程上下文中能夠被搶占,其實表明,像用戶空間內的進程一樣,當前的進程同樣可以被其他進程搶占。因為新的進程可以使用相同的系統(tǒng)調用,所以必須小心,保證該系統(tǒng)調用是可重人的。當然,這也是在對稱多處理中必須同樣關心的問題。

當系統(tǒng)調用返回的時候,控制權仍然在system_call()中,它最終會負責切換到用戶空間并讓用戶進程繼續(xù)執(zhí)行下去。

系統(tǒng)調用訪問示例

操作系統(tǒng)使用系統(tǒng)調用表將系統(tǒng)調用編號翻譯為特定的系統(tǒng)調用。系統(tǒng)調用表包含有實現(xiàn)每個系統(tǒng)調用的函數(shù)的地址。例如,read() 系統(tǒng)調用函數(shù)名為sys_read。read()系統(tǒng)調用編號是 3,所以sys_read() 位于系統(tǒng)調用表的第四個條目中(因為系統(tǒng)調用起始編號為0)。從地址 sys_call_table + (3 * word_size) 讀取數(shù)據(jù),得到sys_read()的地址。

找到正確的系統(tǒng)調用地址后,它將控制權轉交給那個系統(tǒng)調用。我們來看定義sys_read()的位置,即fs/read_write.c文件。這個函數(shù)會找到關聯(lián)到 fd 編號(傳遞給 read() 函數(shù)的)的文件結構體。那個結構體包含指向用來讀取特定類型文件數(shù)據(jù)的函數(shù)的指針。進行一些檢查后,它調用與文件相關的 read() 函數(shù),來真正從文件中讀取數(shù)據(jù)并返回。與文件相關的函數(shù)是在其他地方定義的 —— 比如套接字代碼、文件系統(tǒng)代碼,或者設備驅動程序代碼。這是特定內核子系統(tǒng)最終與內核其他部分協(xié)作的一個方面。

讀取函數(shù)結束后,從sys_read()返回,它將控制權切換給 ret_from_sys。它會去檢查那些在切換回用戶空間之前需要完成的任務。如果沒有需要做的事情,那么就恢復用戶進程的狀態(tài),并將控制權交還給用戶程序。

從用戶空間直接訪問系統(tǒng)調用

通常,系統(tǒng)調用靠C庫支持。用戶程序通過包含標準頭文件并和C庫鏈接,就可以使用系統(tǒng)調用(或者調用庫函數(shù),再由庫函數(shù)實際調用)。但如果你僅僅寫出系統(tǒng)調用,glibc庫恐怕并不提供支持。值得慶幸的是,Linux本身提供了一組宏,用于直接對系統(tǒng)調用進行訪問。它會設置好寄存器并調用陷人指令。這些宏是_syscalln(),其中n的范圍從0到6。代表需要傳遞給系統(tǒng)調用的參數(shù)個數(shù),這是由于該宏必須了解到底有多少參數(shù)按照什么次序壓入寄存器。舉個例子,open()系統(tǒng)調用的定義是:

long open(const char *filename, int flags, int mode)

而不靠庫支持,直接調用此系統(tǒng)調用的宏的形式為:

#define NR_ open 5
syscall3(long, open, const char*,filename, int, flags, int, mode)

這樣,應用程序就可以直接使用open() 對于每個宏來說,都有2+ n個參數(shù)。第一個參數(shù)對應著系統(tǒng)調用的返回值類型。第二個參數(shù)是系統(tǒng)調用的名稱。再以后是按照系統(tǒng)調用參數(shù)的順序排列的每個參數(shù)的類型和名稱。NR open在中定義,是系統(tǒng)調用號。該宏會被擴展成為內嵌匯編的C函數(shù)。由匯編語言執(zhí)行前一節(jié)所討論的步驟,將系統(tǒng)調用號和參數(shù)壓入寄存器并觸發(fā)軟中斷來陷入內核。調用open()系統(tǒng)調用直接把上面的宏放置在應用程序中就可以了。

讓我們寫一個宏來使用前面編寫的foo()系統(tǒng)調用,然后再寫出測試代碼炫耀一下我們所做的努力。

#define NR foo 283
_sysca110(long, foo)
int main()
{
long stack size;
stack_ size=foo();
printf("The kernel stack
size is 81d/n",stack_ size);
return;
}

添加系統(tǒng)調用

通過修改內核源代碼添加系統(tǒng)調用

linux-2.6.*

通過以上分析linux系統(tǒng)調用的過程,

將自己的系統(tǒng)調用加到內核中就是一件容易的事情。下面介紹一個實際的系統(tǒng)調用,

并把它加到內核中去。要增加的系統(tǒng)調用是:inttestsyscall(),其功能是在控制終端屏幕上顯示hello world,

執(zhí)行成功后返回0。

編寫int testsyscall()系統(tǒng)調用–響應函數(shù)

編寫一個系統(tǒng)調用意味著要給內核增加1個函數(shù),將新函數(shù)放入文件kernel/sys.c中。新函數(shù)代碼如下:

asmlingkage sys_testsyscall()
{
print("hello worldn");
return 0;
}

添加系統(tǒng)調用號

編寫了新的系統(tǒng)調用過程后,下一項任務是使內核的其余部分知道這一程序的存在,然后重建包含新的系統(tǒng)調用的內核。為了把新的函數(shù)連接到已有的內核中去, 需要編輯2個文件:

1).inculde/asm/unistd.h在這個文件中加入

#define_NR_testsyscall 191

系統(tǒng)調用表中添加對應項

2).are/i386/kernel/entry.s這個文件用來對指針數(shù)組初始化,在這個文件中增加一行:

.long SYMBOL_NAME(_sys_tsetsycall)

將.rept NR_syscalls-190改為NR_SYSCALLS-191,然后重新編譯和運行新內核。

使用新的系統(tǒng)調用

在保證的C語言庫中沒有新的系統(tǒng)調用的程序段,必須自己建立其代碼如下

#inculde

_syscall0(int,testsyscall)

main()
{
tsetsyscall();
}

在這里使用了_syscall0宏指令,宏指令本身在程序中將擴展成名為syscall()的函數(shù),它在main()函數(shù)內部加以調用。

在testsyscall()函數(shù)中, 預處理程序產(chǎn)生所有必要的機器指令代碼,包括用系統(tǒng)調用參數(shù)值加載相應的cpu寄存器, 然后執(zhí)行int 0x80中斷指令。

linux-3.*

在linux-3.8.4/kernel/sys.c 文件末尾添加新的系統(tǒng)調用函數(shù)如:

asmlinkage int sys_mycall(int number)
{
printk("這是我添加的第一個系統(tǒng)調用");
return number; }

在arch/x86/syscall_32.tbl下找到unused 223號調用然后替換如:

223 i386 mycall sys_mycall

如果是64位系統(tǒng),在arch/x86/syscalls/syscall_64.tbl下找到313號系統(tǒng)調用,然后在其下面加上314號自己的中斷如:`314 common mycall sys_mycall

利用內核模塊添加系統(tǒng)調用

模塊是內核的一部分,但是并沒有被編譯到內核里面去。它們被分別編譯并連接成一組目標文件, 這些文件能被插入到正在運行的內核,或者從正在運行的內核中移走。內核模塊至少必須有2個函數(shù):init_module和cleanup_module。

第一個函數(shù)是在把模塊插入內核時調用的;

第二個函數(shù)則在刪除該模塊時調用。由于內核模塊是內核的一部分,所以能訪問所有內核資源。根據(jù)對linux系統(tǒng)調用機制的分析,

如果要增加系統(tǒng)調用,可以編寫自己的函數(shù)來實現(xiàn),然后在sys_call_table表中增加一項,使該項中的指針指向自己編寫的函數(shù),

就可以實現(xiàn)系統(tǒng)調用。下面用該方法實現(xiàn)在控制終端上打印“hello world” 的系統(tǒng)調用testsyscall()。

編寫系統(tǒng)調用內核模塊

#inculde(linux/kernel.h)

#inculde(linux/module.h)

#inculde(linux/modversions.h)

#inculde(linux/sched.h)

#inculde(asm/uaccess.h)

#define_NR_testsyscall 191

extern viod *sys_call+table[];

asmlinkage int testsyscall()

{
printf("hello worldn");

return 0;

}

int init_module()

{
sys_call_table[_NR_tsetsyscall]=testsyscall;
printf("system call testsyscall() loaded successn");

return 0;
}

void cleanup_module()
{

}

使用新的系統(tǒng)調用

#define_NR_testsyscall 191

_syscall0(int,testsyscall)

main()
{
testsyscall();
}

內核Linux系統(tǒng)調用的列表

以下是Linux系統(tǒng)調用的一個列表,包含了大部分常用系統(tǒng)調用和由系統(tǒng)調用派生出的的函數(shù)。

進程控制

圖片

文件系統(tǒng)控制

文件讀寫操作

圖片

文件系統(tǒng)操作

圖片

系統(tǒng)控制

圖片

內存管理

圖片

網(wǎng)絡管理

圖片

socket控制

圖片

用戶管理

圖片

進程間通信

圖片

信號

圖片

消息

圖片

管道

圖片

信號量

圖片

共享內存

圖片

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 操作系統(tǒng)

    關注

    37

    文章

    6896

    瀏覽量

    123758
  • Linux系統(tǒng)

    關注

    4

    文章

    596

    瀏覽量

    27510
  • 軟件
    +關注

    關注

    69

    文章

    5013

    瀏覽量

    88085
  • 程序
    +關注

    關注

    117

    文章

    3796

    瀏覽量

    81420
收藏 人收藏

    評論

    相關推薦

    Linux系統(tǒng)調用的實現(xiàn)與應用

    在計算機科學中,系統(tǒng)調用(System Call)是一種操作系統(tǒng)提供的服務,它允許應用程序通過軟件中斷的方式訪問操作系統(tǒng)內核中的函數(shù)。這些函數(shù)提供了一系列與硬件相關的服務,例如文件
    發(fā)表于 06-14 11:46 ?557次閱讀

    Linux內核中系統(tǒng)調用詳解

    Linux內核中設置了一組用于實現(xiàn)各種系統(tǒng)功能的子程序,稱為系統(tǒng)調用。用戶可以通過系統(tǒng)調用命令在
    發(fā)表于 08-23 10:37 ?820次閱讀
    <b class='flag-5'>Linux</b>內核中<b class='flag-5'>系統(tǒng)</b><b class='flag-5'>調用</b>詳解

    添加Linux系統(tǒng)調用與利用QEMU測試

    添加Linux系統(tǒng)調用與利用QEMU測試
    發(fā)表于 10-01 12:19 ?821次閱讀
    添加<b class='flag-5'>Linux</b><b class='flag-5'>系統(tǒng)</b><b class='flag-5'>調用</b>與利用QEMU測試

    ARM linux系統(tǒng)調用的實現(xiàn)原理

    大家都知道linux的應用程序要想訪問內核必須使用系統(tǒng)調用從而實現(xiàn)從usr模式轉到svc模式。下面咱們看看它的實現(xiàn)過程。
    發(fā)表于 05-30 11:24 ?2246次閱讀

    基于linux系統(tǒng)實現(xiàn)的vivado調用VCS仿真教程

    linux系統(tǒng)上實現(xiàn)vivado調用VCS仿真教程 作用:vivado調用VCS仿真可以加快工程的仿真和調試,提高效率。 前期準備:確認安裝vivado軟件和VCS軟件 VCS軟件最
    的頭像 發(fā)表于 07-05 03:30 ?1.2w次閱讀
    基于<b class='flag-5'>linux</b><b class='flag-5'>系統(tǒng)</b>實現(xiàn)的vivado<b class='flag-5'>調用</b>VCS仿真教程

    linux操作系統(tǒng)中如何截獲系統(tǒng)調用

    分享到: 使用Linux Kernel Module的一般目的就是擴展系統(tǒng)的功能,或者給某些特殊的設備提供驅動等等。其實利用Linux內核模塊我們還可以做一些比較黑客的事情,例如用來攔截系統(tǒng)
    發(fā)表于 11-07 09:58 ?0次下載

    透了解系統(tǒng)調用助你成為Linux下編程高手

    Linux內核中設置了一組用于實現(xiàn)各種系統(tǒng)功能的子程序,稱為系統(tǒng)調用。用戶可以通過系統(tǒng)調用命令在
    的頭像 發(fā)表于 05-11 11:27 ?3473次閱讀
    透了解<b class='flag-5'>系統(tǒng)</b><b class='flag-5'>調用</b>助你成為<b class='flag-5'>Linux</b>下編程高手

    學會Linux添加自定義系統(tǒng)調用

    Linux的世界里,我們經(jīng)常會遇到系統(tǒng)調用這一術語,所謂系統(tǒng)調用,就是內核提供的、功能十分強大的一系列的函數(shù)。
    發(fā)表于 05-13 11:39 ?899次閱讀

    你知道Linux系統(tǒng)調用的原理

    系統(tǒng)調用是應用程序與操作系統(tǒng)內核之間的接口,它決定了程序如何與內核打交道的。無論程序是直接進行系統(tǒng)調用,還是通過運行庫,最終還是會到達
    發(fā)表于 05-16 16:21 ?1525次閱讀
    你知道<b class='flag-5'>Linux</b><b class='flag-5'>系統(tǒng)</b><b class='flag-5'>調用</b>的原理

    Linux系統(tǒng)調用的技巧

    1.linux系統(tǒng)調用的基本原理  linux系統(tǒng)調用形式與POSIX兼容,也
    發(fā)表于 04-02 14:36 ?417次閱讀

    Linux系統(tǒng)調用是什么

    所謂系統(tǒng)調用是指操作系統(tǒng)提供給用戶程序調用的一組“特殊”接口,用戶程序可以通過這組“特殊”接口獲得操作系統(tǒng)內核提供的服務。例如,用戶可以通過
    發(fā)表于 06-11 09:33 ?2375次閱讀

    如何區(qū)分xenomai、linux系統(tǒng)調用/服務

    對于同一個POSIX接口應用程序,可能既需要xenomai內核提供服務(xenomai 系統(tǒng)調用),又需要調用linux內核提供服務(linux
    的頭像 發(fā)表于 05-10 10:28 ?2166次閱讀

    Linux內核系統(tǒng)調用概述及實現(xiàn)原理

    本文介紹了系統(tǒng)調用的一些實現(xiàn)細節(jié)。首先分析了系統(tǒng)調用的意義,它們與庫函數(shù)和應用程序接口(API)有怎樣的關系。然后,我們考察了Linux內核
    的頭像 發(fā)表于 05-14 14:11 ?2263次閱讀
    <b class='flag-5'>Linux</b>內核<b class='flag-5'>系統(tǒng)</b><b class='flag-5'>調用</b><b class='flag-5'>概述</b>及實現(xiàn)原理

    Linux系統(tǒng)調用的具體實現(xiàn)原理

    文我將基于 ARM 體系結構角度,從 Linux 應用層例子到內核系統(tǒng)調用函數(shù)的整個過程來梳理一遍,講清楚linux系統(tǒng)
    的頭像 發(fā)表于 09-05 17:16 ?1142次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>系統(tǒng)</b><b class='flag-5'>調用</b>的具體實現(xiàn)原理

    Linux系統(tǒng)調用腳本的常見方法

    linux系統(tǒng)中有多種方法可以在系統(tǒng)啟動后調用腳本,接下來介紹幾種常見的方法
    的頭像 發(fā)表于 12-13 18:16 ?1100次閱讀
    江津市| 百家乐官网对子的玩法| 百家乐官网号破| 大发888真人真钱赌博| 百家乐官网玩法及技巧| 百家乐官网博赌场| 哪个百家乐技巧平台信誉好| 顶级赌场怎么样| 百家乐官网正负计| 博九百家乐的玩法技巧和规则 | 免费百家乐官网过滤| 大发888东方鸿运娱乐| 真人百家乐官网软件博彩吧| 百家乐西园二手房| 圣淘沙娱乐城真人赌博| 百家乐投注组合| 云顶娱乐| 属蛇和属马合作做生意谁吃亏| bet365金融| 百家乐官网游戏筹码| 娱乐城免费送体验金| 潘多拉百家乐官网的玩法技巧和规则 | 百家乐官网线上游戏| 百家乐系列抢庄龙| 百家乐官网投注办法| 百家乐二十一点| 百家乐官网玩法皇冠现金网| 新加坡百家乐的玩法技巧和规则| 澳门百家乐官网娱乐城送体验金| 千亿娱百家乐的玩法技巧和规则| 赌博百家乐官网赢钱方法| 巴西百家乐的玩法技巧和规则| 缅甸百家乐官网网络赌博解谜| 百家乐是个什么样的游戏 | 百家乐官网l23| 大发888捕鱼| 永利百家乐官网的玩法技巧和规则 | 新澳门百家乐官网娱乐城| 百利宫百家乐的玩法技巧和规则 | 博E百百家乐官网的玩法技巧和规则 | 百家乐路纸计算|