1. 程式人生 > >系統呼叫過程詳解

系統呼叫過程詳解

在上一篇部落格中,我們介紹了庫函式和系統呼叫的聯絡和區別。在這篇部落格中,我們將通過分析Linux0.11的原始碼來理解系統呼叫的實際執行過程。

整個過程如下:首先指令流執行到系統呼叫函式時,系統呼叫函式通過int 0x80指令進入系統呼叫入口程式,並且把系統呼叫號放入%eax中,如果需要傳遞引數,則把引數放入%ebx,%ecx和%edx中。進入系統呼叫入口程式(System_call)後,它首先把相關的暫存器壓入核心堆疊(以備將來恢復),這個過程稱為保護現場。保護現場的工作完成後,開始檢查系統呼叫號是不是一個有效值,如果不是則退出。接下來根據系統呼叫號開始呼叫系統呼叫處理程式(這是一個正式執行系統呼叫功能的函式),從系統呼叫處理程式返回後,就會去檢查當前程序是否處於就緒態、程序時間片是否用完,如果不在就緒態或者時間片已用完,那麼就會去呼叫程序排程程式schedule(),轉去執行其他程序。如果不執行程序排程程式,那麼接下來就會開始執行ret_from_sys_call,顧名思義,這這個程式主要執行一些系統呼叫的後處理工作。比如它會去檢查當前程序是否有需要處理的訊號,如果有則去呼叫do_signal(),然後進行一些恢復現場的工作,返回到原先的程序指令流中。至此整個系統呼叫的過程就結束了。

注:關於程序排程函式schedule()和訊號處理函式do_signal()我們會在後面的部落格中具體進行講解。

好了,知道了系統呼叫的大致執行過程,下面我們來看看Linux0.11核心是如何實現這一功能的。

我們以Linux0.11核心中init/main.c中的fork()函式為例(fork()是一個系統呼叫)。

在main.c中有這麼幾行程式碼:

這裡的_syscall0表示內嵌巨集程式碼,(不明白?沒關係),我們只要知道執行fork(),就相當於執行_syscall0(),它的定義在include/Unistd.h中:

這是一段嵌入在C語言的彙編程式碼(注:在Linux核心中有很多這樣的程式碼,為的是在某些關鍵的地方使用匯編程式碼提供執行效率)。這段程式碼的大致含義是:執行int 0x80,把系統呼叫號放入%eax。這個時候系統就自動進入了系統呼叫入口程式_system_call中,它在kernel/System_call.s中:

第81-82行的程式碼是判斷系統呼叫號是不是有效值。從第83-93就在進行一些入棧的操作,緊接著第94行程式碼執行call _sys_call_table(,%eax,4),這個_sys_call_table是什麼東西呢?它就是系統呼叫處理函式的指標陣列,它裡面包括了所有的系統呼叫。這個指標陣列定義在include/linux/Sys.h,截個圖給大家看看

看到沒?我們要呼叫的fork()它的處理函式就是sys_fork(),它處在第3個位置上,所以當我們執行call _sys_call_table(,%eax,4)時,它就自動去執行sys_fork()啦!從系統呼叫處理函式返回後,第96-100的程式碼會去判斷需不需要執行排程函式schedule。如果不需要的話,就開始執行ret_from_sys_call了,它同樣在kernel/System_call.s中:

這個函式會去檢查是否有訊號需要處理,如果有則呼叫do_signal函式。標號3那個位置的彙編程式碼就是進行一些恢復現場的工作。

Linux0.11核心的系統呼叫的整個過程大概就是這樣!