1. 程式人生 > >《Linux作業系統分析》之使用庫函式API和C程式碼中嵌入彙編程式碼兩種方式使用同一個系統呼叫

《Linux作業系統分析》之使用庫函式API和C程式碼中嵌入彙編程式碼兩種方式使用同一個系統呼叫

本篇文章分析的是使用庫函式API和C程式碼中嵌入彙編程式碼兩種方式使用同一個系統呼叫,來說明在Linux系統中,系統呼叫的實現機制。

相關知識

首先關於這篇文章會介紹一些用到的知識。

一、什麼是核心態,什麼又是使用者態。

核心態:在高執行級別下,程式碼可以執行特權指令,訪問任意的實體地址。

使用者態:在相應的低執行狀態下,程式碼的掌控範圍受到限制,只能在對應級別允許的範圍內活動。



當一個程式在使用者態下執行的時,它不能直接訪問核心資料結構或者核心的程式。然而,當應用程式執行在核心態下的時候,這些限制不再有效。每中CPU模型都為從使用者態到核心態的轉換提供了特殊的指令,反之亦然。一個程式的執行大部分時間都處於使用者態下,只有需要核心所提供的服務的時候才切換到核心態。當核心滿足了使用者程式的請求後,它讓程式又回到使用者態下。

二、C程式碼中嵌入彙編程式碼

嵌入式彙編的格式如下:


下面是一些常用的嵌入彙編的限定符:


彙編到底有什麼好處呢?
 1.提高速度和效率。(不過這種情況很少了,現在C/C++編譯器的優化很厲害了)
 2.實現某些C語言中不具備、但為不同的機器所特有的功能。(這是主要原因)
 3.利用通用的組合語言例程。(也常會遇到)

三、中斷機制

但是在這裡我們需要在介紹一下,中斷和系統呼叫之間的關係。

系統呼叫是隻是一種特殊的中斷,通過trap陷阱,進入到核心態。其中包括以下的步驟。


四、應用介面程式設計和系統呼叫

應用介面程式設計是一個函式的定義,說明了如何獲得一個給定的服務。

系統呼叫是通過軟中斷向核心態發出了一個明確的請求。

系統呼叫的意義:作業系統為使用者態程序與硬體裝置進行互動提供的一組介面。可以把使用者從底層的硬體變成中解放出來,極大的提高了系統的安全性,使使用者程式具有可移植性。


上圖是系統呼叫的三個層次依次是:xyz函式(API)、system_ call(中斷向量)和 sys_ xyz(中斷服務程式)。

分析過程

在這裡我呼叫getpid函式。當然也可以去看看還有什麼其他的系統呼叫點選

使用庫函式的實現程式碼如下:

#include<stdio.h>
#include<unistd.h>
int main(){
pid_t num;
num = getpid();
printf("this is process id :%u\n",num);
return 0;
}
然後進行編譯,並執行,結果如下圖:


我們可以得到如上的結果。

我們修改程式碼,使用嵌入的彙編來呼叫20號系統呼叫。

#include <stdio.h>
#include<unistd.h>
int main()
{
    pid_t pid;
    asm volatile(
        "movl $0,%%ebx\n\t"//將ebx暫存器清零,系統呼叫傳遞第一個引數使用ebx,這裡是null
        "movl $0x14,%%eax\n\t"//將0xd放入eax中,0x14為20,傳遞系統呼叫號20
        "int $0x80\n\t"
        "movl %%eax,$0\n\t"//通過eax這個暫存器返回系統呼叫值,和普通函式一樣
        :"=m"(pid)
    );
    printf("this process's pid is : %u\n",pid);
    return 0;
}

編譯後執行結果和第一種情況一樣。

總結:

Linux中是通過暫存器%eax傳遞系統呼叫號,所以具體呼叫fork的過程是:將20存入%eax中,然後進行系統呼叫.對於引數傳遞,Linux是通過暫存器完成的。Linux最多允許向系統呼叫傳遞6個引數,分別依次由%ebx,%ecx,%edx,%esi,%edi和%ebp這個6個暫存器完成。程序執行在使用者態和核心態時使用不同的棧,分別叫做使用者棧和核心棧。兩者各自負責相應特權級別狀態下的函式呼叫。系統呼叫時,程序不僅是使用者態到核心態的切換,同時也要切換棧,這樣核心態的系統呼叫才能在核心棧上完成呼叫。系統呼叫返回時,還要切換回使用者棧,繼續完成使用者態下的函式呼叫。

備註:

楊峻鵬 + 原創作品轉載請註明出處 + 《Linux核心分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000