1. 程式人生 > >Linux核心分析(五)系統呼叫過程解析

Linux核心分析(五)系統呼叫過程解析

禹曉博+ 原創作品轉載請註明出處 + 歡迎加入《Linux核心分析》MOOC網易雲課堂學習

一、系統呼叫流程分析

        系統呼叫系統呼叫就是使用者空間應用程式和核心提供的服務之間的一個介面。由於服務是在核心中提供的,因此無法執行直接呼叫;相反,我們必須使用一個程序來跨越使用者空間與核心之間的界限,這實際上就是系統呼叫的過程。我們首先來開一個系統呼叫流程的示意圖。


        Linux中的系統呼叫的實現會根據不同的架構而有所變化,而且即使在某種給定的體系結構上也會有所不同,我們看到上述是一個簡單的系統呼叫的例子。每一個個系統呼叫都是通過一個單一的入口點多路傳入核心的。eax 暫存器用來標識應當呼叫的某個系統呼叫,這在 C 庫中做了指定(來自使用者空間應用程式的每個呼叫)。當載入了系統的 C 庫呼叫索引和引數時,就會呼叫一個軟體中斷(0x80 中斷),它將執行 system_call 函式(通過中斷處理程式),這個函式會按照 eax 內容中的標識處理所有的系統呼叫。在經過幾個簡單測試之後,使用 system_call_table 和 eax 中包含的索引來執行真正的系統呼叫了。從系統呼叫中返回後,最終執行 syscall_exit,並呼叫 resume_userspace 返回使用者空間。然後繼續在 C 庫中執行,它將返回到使用者應用程式中。

        上一篇文章實際上我們經講過了關於system_call_table相關的內容了,我們實際上可以簡單的理解為一個跳轉列表一樣的東西,那麼系統是如何通過這些列表來找到具體的服務程式的呢?這裡面實際上非常類似於中斷處理的過程,因為系統呼叫過程的本身就是引發一次中斷的過程。這裡面有一個非常好的詞就是trap。實際上這是一次主動引起的異常(大體上可以這麼理解)。那麼在分析之前我們首先先看這個system_call_table是如何參與系統呼叫過程的呢?我們來看這樣一張圖。


        首先我們分析一下這個過程,首先在使用者空間中,我們編寫的程式中有一個系統呼叫的過程,這個時候首先他會進入到系統呼叫的控制程式中去,在這個過程中首先要做的就是儲存現場,然後就是要定位到這個系統呼叫列表中去。我們看到這個過程中首先程式會找到sys_call_table的基地址然後在進行相應的運算之後得出一個偏移地址,這裡面是採用eax*4來得到這樣一個地址的。入口地址實際上就是系統服務例程的基地址,我們具體需要什麼樣的功能實現都在這個服務程式中。


        上面分析我們知道實際上eax就是提供一個索引來確定要呼叫sys_call_table中那個表項對用的系統呼叫。上圖中就是一些具體的例子。其中的offset就是eax提供的索引編譯當然是計算過的。然後是對應的symbol和表項,最後是系統中他們所在的位置。

        那麼接下來問題就來了,上面我們僅僅是知道系統呼叫過程中的上面部分的過程情況,那麼在系統呼叫的執行過程中,具體程式碼的執行過程是什麼樣子的呢?這裡面我們結合第四章講義System Call(可以到網易雲課堂中孟寧老師的Linux核心分析課程中下載)最後給出的系統呼叫過程的彙編虛擬碼以及實際的執行過程來分析一下system_call具體的執行過程。首先我們還是來看一張流程圖(為什麼這麼多圖呢吐舌頭

,因為這樣可以少寫點字,省得麻煩呀,圖的形式更為清晰也容易理解)


       我們看到這裡面程序的排程時機發生在返回之前,有可能會有一些其他的外部請求來引起這樣的程序排程過程,這裡面我們實際上是結合講義中的虛擬碼以及一些擴充的細節來完成的這個過程圖。就像視訊中所講的那樣實際上在系統從使用者態陷入核心態的時候(trap~)就是開始呼叫的時機,然後系統就會按照上述的過程(實際上第一章圖片是一個總體的描述,大家可以和流程圖結合起來看)進行,然後再返回到使用者態下。上面的圖畫的實在辛苦哦應該省去大家不少閱讀時間吧,如果有什麼錯誤歡迎指正出來啊。

二、實驗過程描述

       下面我們來一起看一下實驗過程,很不幸的是系統呼叫過程的內部跟蹤在gdb下是不能完全跟蹤的,所以我們接下來的過程就是敘述一下在gdb下進行核心跟蹤的過程。

        首先,我們先刪除一下之前我們的menuOS的檔案,因為這次我們更新了其中的命令。用於跟進系統呼叫過程,加入了time這樣一個選項,這個地方我們可以發起一個斷點然後跟蹤系統呼叫過程。


       上面設個圖我們就是刪除之前的系統檔案的過程然後從github上更新這次實驗要用的新的menuOS映象。然後我們開始製作根檔案系統。


        之後系統就成功啟動了,我們就會開到熟悉的介面。如果是要跟蹤除錯系統的話我們可以用這個命令:

:~/$qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S  


       同樣的我們再開啟一個視窗然後進入gdb除錯模式,然後我們首先是要載入它的符號表,命令是 file linux3.18.6/vmlinux 然後在連線到他,命令是:target remote:1234這樣我們就可以開始除錯他了。


        然後我們添加了一個斷點在sys_time上,然後我們按c繼續執行我們開到系統停在了相應的位置上。


        這個時候我們可以輸入list進行檢視對應部分的程式碼。這裡面有一個系統呼叫額定SYSCALL_DEFINE裡面的time就是我們寫的那個time,當然這裡面也可以換成其他的比如上週我們自己寫內容,只要修改test.c裡面的內容就可以了。


        這個時候我們再看一下我們的menuOS介面的情況,如下圖所示:這個時候我們輸入time他就會停在那個地方了以為設定過斷點,然後我們可以用s進行單步除錯。那個再之後就是我們上面所說的系統呼叫過程了。


三、總結

        經過了兩個實驗的過程我們已經大體上對系統呼叫的過程有所瞭解,實際上這個過程還比我們所說的更為複雜,之後我會有時間慢慢擴充它。我們知道作業系統為在使用者態執行的程序與硬體裝置進行互動提供了一組幾口。在應用程式與硬體之間設定一個額外的層實際上是有很多優點的——所謂的系統呼叫。首先這使得程式設計更加容易,把使用者從學習硬體裝置的低階程式設計特性中解放出來。其次,著極大的提高了系統的安全性,因為核心在試圖滿足某個請求之前在介面級別上就可以檢查這種請求是否是正確的合法的。最後更為重要的是,這些介面是的程式更具有可以執行,因為只要核心所提供的一組介面相同,那麼在任意一個核心之上就可以正確的編譯和執行我們編寫的程式了。

        Linux系統就是通過核心發出的系統呼叫(system call)實現了使用者態程序和硬體裝置之間的大部分介面。最後我們還是以一張圖來從很抽象的層次說明這件事情。