Linux基礎 系統呼叫

2021-10-09 05:02:30 字數 4138 閱讀 8355

現代作業系統中程式本身沒有多少權利訪問系統資源,為了保護系統資源,os會組織程式直接訪問系統資源,比如檔案、網路、io、各種裝置等。

但是有些場景不借助os沒法很好地辦到,比如讓程式等待一段時間,如果借助os,我們可以用sleep(),但是要是自己寫的話,可能就是如下這樣:

for

(int i=

0;i<

1000000

;i++

);

可以是可以,但是這樣會浪費cpu時間。特別是這段程式在不同頻率的cpu上耗費時間不一樣:

因此這樣絕不是乙個好辦法。為了讓程式更好地借助os實現一些操作,os會提供一套介面,讓程式使用。這些介面往往通過中斷來實現,比如linux使用0x80號中斷作為系統呼叫入口

使用不便。os提供的介面比較原始,沒有很好地包裝

各個os系統呼叫不相容。linux、windows、unix都不同

為了解決這個問題,我們可以在系統呼叫和程式之間做一層抽象層來統一管理,這就是執行庫。比如對於讀取檔案,我們可以用c語言中的fread函式,其底層在windows下函式實現可能是readfile,linux下可能是read系統呼叫,但是沒關係,不管在哪個平台,都可以用fread來讀檔案。

現代cpu可以在多種截然不同特權級別下執行指令,現代os有使用者模式(user mode)核心模式(kernel mode),也就是使用者態核心態。普通程式執行在使用者態,對操作有很多限制,比如沒法直接訪問硬體裝置、開關中斷、改變特權模式等。

使用者態程式想要執行核心態**一般要通過中斷(interrupt)來從使用者態切換到核心態。**中斷就是乙個硬體或者軟體發出的請求,要求cpu暫停當前工作轉手去處理更加重要的事情。**舉個例子,我們在編輯檔案的時候,鍵盤上的鍵按下,cpu如何獲知這一點呢?

第一種是輪詢(poll),cpu每隔一小段時間(幾十到幾百毫秒)詢問鍵盤是否被按下,大部分情況都是沒有鍵被按下的回應,這樣就很浪費cpu時間

第二種是發訊號,cpu不理睬鍵盤,鍵盤被按下之後,鍵盤晶元給cpu傳送乙個訊號,cpu接受到訊號去詢問哪個鍵被按下。這個訊號就是乙個中斷

中斷有兩個屬性,提及中斷要有中斷號(從0開始)和中斷處理程式(interrupt service routine,isr),類似於訊號,有訊號id和訊號處理程式。核心裡有乙個中斷向量表(interrupt vector table),這個陣列的第n項表示第n號中斷的中斷處理程式。中斷來了之後,cpu暫停當前**執行並儲存當前上下文,根據中斷號查詢對應的處理程式呼叫。(這裡涉及程序上下文切換)

中斷有兩種:

硬體中斷:硬體異常或者電源斷電、鍵盤被按下等

軟體中斷:軟體中斷通常是指令,帶有乙個引數記錄中斷號,使用這個指令使用者可以手動觸發某個中斷並執行其中斷處理程式。比如在i386下,int 0x80這個指令會去執行0x80號中斷處理程式

對於中斷和系統呼叫又如何繫結的呢?

由於中斷號很有限,os不會讓乙個中斷號對應乙個系統呼叫。linux用int 0x80來觸發所有系統呼叫。那麼對於同乙個中斷號,os怎麼知道哪乙個系統呼叫要執行呢?首先系統呼叫號會被放入乙個固定的暫存器eax,使用者把系統呼叫號傳入eax,然後使用int 0x80呼叫中斷,中斷服務程式就可以eax取得系統呼叫號,進而呼叫對應的函式。

下面以fork(系統呼叫號為2)為例來看系統呼叫如何執行的:

一步一步看細節:

觸發中斷

程式在**中呼叫乙個系統呼叫時,用函式來實現的:

int

main()

fork是對系統呼叫fork的封裝,可以用下面這個巨集來定位它:

_syscall0

(pid_t,fork)

;

_syscall0是乙個巨集函式,用來定義乙個沒有引數的系統呼叫的封裝。引數意義:

pid_t:表示系統呼叫返回值型別,代表程序id

fork:表示這個系統呼叫名稱是fork

這個巨集定義展開之後如下帶有at&t格式的彙編**的程式:

pid_t fork

(void

)

翻譯一下就是:

pid_t fork

(void

)

__nr__fork是fork的系統呼叫號2,__syscall_return是另乙個巨集,用於檢查系統呼叫返回值。

當使用者呼叫某個系統呼叫的時候,實際是執行了以上一段彙編**。cpu執行到int $0x80時,會儲存現場以便恢復,接著會將特權狀態切換到核心態。然後cpu便會查詢中斷向量表中的第0x80號元素。

切換堆疊

在實際執行中斷向量表中的第0x80號元素所對應的函式之前,cpu首先還要進行棧的切換。在linux中,使用者態和核心態使用的是不同的棧,兩者各自負責各自的函式呼叫,互不干擾。但在應用程式呼叫0x80號中斷時,程式的執行流程從使用者態切換到核心態,這時程式的當前棧必須也相應地從使用者棧切換到核心棧從中斷處理函式中返回時,程式的當前棧還要從核心棧切換回使用者棧。所謂的「當前棧」,指的是esp的值所在的棧空間。如果esp的值位於使用者棧的範圍內,那麼程式的當前棧就是使用者棧,反之亦然。此外,暫存器ss的值還應該指向當前棧所在的頁。

所以,將當前棧由使用者棧切換為核心棧的實際行為就是:

儲存當前的暫存器espss的值。

espss的值設定為核心棧的相應值。

反過賴將當前棧從核心態轉為使用者態就是:

0x80號中斷發生的時候,cpu除了切入核心態之外,還會自動完成下列幾件事:

找到當前程序的核心棧(每。 個程序都有自己的核心棧)。

在核心棧中依次壓入使用者態的暫存器ssespeflagscseip.

而當核心從系統呼叫中返回的時候,須要呼叫iret指令來回到使用者態,iret指令則會從核心棧裡彈出暫存器ssespeflagscseip的值,使得棧恢復到使用者態的狀態。這個過程可以用下圖來表示。

中斷處理程式

棧切換完成之後,程式流程就切換到了中斷向量表中記錄的0x80號中斷處理程式。

具體見《程式設計師的自我修養》,下面直接上圖呼叫流程:

linux核心基礎(系統呼叫,簡明)

核心基礎 系統呼叫 在說系統呼叫之前,先來說說核心是怎麼和我們互動的,或者說是怎麼和我們產生交集的。首先,核心是用來控制硬體的只有核心才能直接控制硬體,所以說核心很重要,如果核心被控制那麼電腦的一切都被控制了,所以我們需要把核心保護起來,所以shell 就誕生了,我們絕大多數情況下是在和shell ...

linux核心基礎(系統呼叫,簡明)

核心基礎 系統呼叫 在說系統呼叫之前。先來說說核心是怎麼和我們互動的。或者說是怎麼和我們產生交集的。首先,核心是用來控制硬體的僅僅有核心才幹直接控制硬體,所以說核心非常重要,假設核心被控制那麼電腦的一切都被控制了,所以我們須要把核心保護起來。所以shell 就誕生了,我們絕大多數情況下是在和shel...

linux 系統呼叫

使用者應用可以通過兩種方式使用系統呼叫。第一種方式是通過c庫函式,包括系統呼叫在c庫中的封裝函式和其他普通函式。圖5.2 使用系統呼叫的兩種方式 第二種方式是使用 syscall巨集。2.6.18版本之前的核心,在include asm i386 unistd.h檔案中定義有7個 syscall巨集...