1. 程式人生 > >2018-2019-1 20189206 《Linux內核原理與分析》第五周作業

2018-2019-1 20189206 《Linux內核原理與分析》第五周作業

一個 edi 移植 兩個 特殊 api函數 是我 進程地址空間 傳遞方式

linux內核分析學習筆記 ——第四章 系統調用的三層機制

學習重點——系統調用


用戶態、內核態和中斷

  • Intel x86 CPU有四種不同的執行級別,分別是0,1,2,3其中數字越小,特權越高。
    • Linux操作系統只采用了其中的0和3兩個特權級別,分別對應內核態和用戶態。
      • 內核態:對應高執行級別,代碼可以執行特權指令,訪問任意物理內存,CPU執行級別對應的內核態。
        內核態的CS:EIP指向範圍是任意地址
      • 用戶態:對應底執行級別,代碼能夠掌控的範圍會受到限制。
        用戶態時,以32位x86機器為例,4G的進程地址空間只能訪問0x000000~0xbfffffff的地址空間。
    • 每個進程都有一個4G大小的虛擬地址空間,在這個4G大小的虛擬地址空間中,前0~3G為用戶空間,
      每個進程的用戶空間之間是相互獨立的,互不相幹。

以下圖示表示x86 32位機器的進程內存

技術分享圖片

  • 中斷
    • 進入內核態一般是由終端觸發的,以下時進入內核態的兩種情況
      • 硬件中斷,在用戶進程執行時,硬件中斷信號到來進入內核態,就會執行這個中斷對應的中斷服務歷程。
      • 用戶態程序執行過程中,調用了一個系統中斷,陷入內核態(Trap)。系統調用就是一種特殊的中斷
    • 從用戶態到內核態的寄存器上下文的切換
      • 從用戶態切換到內核態,將用戶態寄存器的上下文保存起來,同時將內核態寄存器的值放入當前CPU中。
      • int指令觸發中斷機制
        • 會在內核棧內保存一些寄存器的值
          • 包括 用戶態棧頂地址%esp 當前狀態字 當前CS:EIP的值
          • 同時會將內核態的棧頂地址、內核態狀態字放入CPU對應的寄存器中。CS:EIP指向中斷處理程序的入口,對於系統來講就是system_call
    • 中斷處理的過程

      Linux系統調用通過中斷向量0x80實現 int 0x80 ,中斷保存了用戶態CS:EIP的值,及當前堆棧段寄存器的棧頂(註意在這裏的棧頂是用戶棧的棧頂,入棧到內核棧),EFLAGS寄存器的當前值保存到內核堆棧。利用int指令將系統調用的中斷服務程序的入口加載在CS:EIP中。

      • 完成中斷服務程序,發生進程調度
        • 如果沒有發生進程調度,直接恢復現場,iret回到原來的狀態。
        • 如果發生了進程調度,當前這些狀態暫時保存在內核堆棧中,下一次發生進程調度再切換回當前進程。

技術分享圖片

系統調用

概述

系統調用的意義是操作系統為用戶態進程與硬件設備之間進行交互提供了一組接口。

操作系統的主要功能是為管理硬件資源和為應用程序開發人員提供良好的環境來使應用程序具有更好的兼容性,為了達到這個目的,內核提供一系列具備預定功能的多內核函數,通過一組稱為系統調用(system call)的接口呈現給用戶。系統調用把應用程序的請求傳給內核,調用相應的的內核函數完成所需的處理,將處理結果返回給應用程序

  • 把用戶從底層的硬件編程中解放出來
  • 極大的提高了系統的安全性
  • 使用戶程序具有可移植性

系統調用的3層機制

系統調用的庫函數就是我們使用的操作系統提供的API,系統調用是通過軟中斷向內核發出中斷請求,int指令觸發中斷請求。

libc函數庫內部定義的一些API內部就使用了系統調用的封裝歷程。

每個系統調用對應一個系統調用的封裝例程,函數庫再使用這些封裝例程定義給出程序員可以調用的API一個API可能只對應一個系統調用,也可能由多個系統調用實現。

技術分享圖片

如上圖所示,在用戶態中

  • 用戶態中的xyz()函數屬於API函數
  • 該API中SYSTEMCALL就是一個系統調用的封裝例程由操作系統給,出會觸發int $0x80中斷

在內核態中

  • 觸發中斷後進入內核態,system_call對應內核代碼的起點,即中斷向量0x80對應的終端服務程序的入口
  • 內核代碼中的sys_xyz()系統調用處理函數
    • 處理結束後,如果發生進程調度會ret_from_sys_call
    • 如果沒有發生進程調度,會執行iret返回用戶態繼續執行

觸發系統調用及參數傳遞方式

  • 觸發系統調用的方式
    • 當用戶執行系統調用時,CPU切換到內核態執行system_call這是中斷的入口函數也是內核代碼的起點。
    • linux中使用0x80觸發系統調用所對應的中斷異常。
    • 內核通過給每個系統調用一個編號來區分,即系統調用號,實現將API函數xyz()和系統調用內核函數sys_xyz()
    • 用戶進程必須指明需要哪一個系統調用,需要使用EAX寄存器
  • 參數傳遞的方式
    • 系統調用從用戶態切換到內核態時使用的不同的堆棧,所以參數的傳遞無法通過參數壓棧的方式進行傳遞。
    • 參數按照順序賦值給EBX ECX EDX ESI EDI EBP 參數的個數不能超過6個寄存器。如果參數過多,就把寄存器作為指針指向內存,以傳遞更多的參數

實驗:使用兩種方法實現觸發系統調用

使用庫函數API觸發系統調用

技術分享圖片

利用上面的代碼實現的是查看當前進程的pid和其父進程的pid,首先是利用庫函數提供的API實現查看。下面是運行結果。

技術分享圖片

使用內嵌匯編的方式實現系統調用

將上面的調用API轉換成內嵌匯編代碼的方式。通過查找可以看到對應的操作系統給出的系統調用的封裝對應的系統調用號
技術分享圖片

技術分享圖片

技術分享圖片

下面對匯編代碼解釋其含義
movl $0,%%ebx\n\t表示傳入參數,這裏不需要傳遞參數,就將EBX寄存器清零
movl $0x14,%%eax\n\tEAX用於傳遞系統調用號,表示這裏調用的是20號系統調用。
int $0x80\n\t是觸發系統調用陷入內核執行20號系統調用的內核處理函數
movl %eax,%0\n\t系統調用會有一個返回值,通過EAX寄存器返回。
這樣就完成了系統調用。

技術分享圖片

通用的觸發系統調用函數

當libc沒有提供某個系統調用的封裝,可以利用libc提供的syscall函數直接調用

技術分享圖片

技術分享圖片

2018-2019-1 20189206 《Linux內核原理與分析》第五周作業