Linux核心分析之四——系統呼叫的工作機制
作者:姚開健
原創作品轉載請註明出處
《Linux核心分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000
學過計算機作業系統的都知道,CPU工作時有兩種狀態,一種是使用者態,一種是核心態,使用者態意味著程式碼訪問的範圍會受到限制,在32位X86的機器上,4G的記憶體裡,在使用者態的時候,只能訪問0x00000000-0xbfffffff的地址空間。而核心態則不受限制,可以訪問任意記憶體地址。當程式在使用者態執行時,如果需要進入核心態執行,則會產生一箇中斷,然後系統進行中斷處理,先是儲存當前執行現場,將使用者態棧頂指標,狀態字,cs:eip值壓棧:
然後進行中斷處理程式,等處理程式執行結束後,則需要恢復現場:
當我們進行程式設計時,可以利用系統封裝好的API來間接進行呼叫系統呼叫。一個API裡面可能對應一個系統呼叫,也可能一個也沒有,封裝例程會將系統呼叫封裝好,一個例程往往對應一個系統呼叫,當用戶程式在執行API(xyz())呼叫時,將會進入封裝例程(xyz())中,由封裝例程裡的中斷彙編程式碼呼叫系統呼叫處理程式(system_call),然後系統呼叫處理程式呼叫系統呼叫服務程式(sys_xyz()):
這可以簡化為系統呼叫三層皮:API,中斷向量所指向的系統呼叫處理程式System_Call,中斷服務程式Sys_xyz()。
進行系統呼叫時,如跟函式呼叫一般,也需要進行引數傳遞。系統呼叫至少有一個引數,就是系統呼叫號,通常會把它 傳入eax暫存器,其他引數則傳入其他暫存器,最多為6個引數,如果引數超過6個,則把最後一個暫存器該為指向一塊剩餘引數記憶體的指標。
下面通過兩個程式來演示系統呼叫的工作機制:
程式一是直接呼叫sysinfo系統呼叫,返回系統資訊,程式二則通過彙編程式碼呼叫系統呼叫,展示引數傳遞過程。
這是編譯執行後程序一的執行結果:# include <stdio.h> # include <sys/sysinfo.h> int main(int argc, char *argv[]) { struct sysinfo *info; error = sysinfo(info); printf("\n\ncode error = %d\n", error); printf("uptime: %d\n" "tatal ram: %d\n" "free ram: %d\n" "shared ram: %d\n" "buffer ram: %d\n" "tatal swap: %d\n" "free swap: %d\n" "process num: %d\n" "tatal high: %d\n" "free high: %d\n" "mem_unit: %d\n", sys->uptime, sys->tatalram, sys->freeram, sys->sharedram, sys->bufferram, sys->tatalswap, sys->freeswap, sys->procs, sys->tatalhigh, sys->freehigh, sys->mem_unit ); return 0; }
程式二:
# include <stdio.h>
# include <sys/sysinfo.h>
int
main(int argc, char *argv[])
{
struct sysinfo *info;
int error;
asm volatile(
"mov %1, %%ebx\n\t"
"mov $0x74, %%eax\n\t"
"int $0x80\n\t"
"mov %%eax, %0\n\t"
: "=m" (error)
: "b" (info)
);
printf("\n\ncode error = %d\n", error);
printf("uptime: %d\n"
"tatal ram: %d\n"
"free ram: %d\n"
"shared ram: %d\n"
"buffer ram: %d\n"
"tatal swap: %d\n"
"free swap: %d\n"
"process num: %d\n"
"tatal high: %d\n"
"free high: %d\n"
"mem_unit: %d\n",
sys->uptime, sys->tatalram,
sys->freeram, sys->sharedram,
sys->bufferram, sys->tatalswap,
sys->freeswap, sys->procs,
sys->tatalhigh, sys->freehigh,
sys->mem_unit
);
return 0;
}
編譯執行程式二的執行結果:
由程式二里彙編程式碼:
asm volatile(
"mov %1, %%ebx\n\t"
"mov $0x74, %%eax\n\t"
"int $0x80\n\t"
"mov %%eax, %0\n\t"
: "=m" (error)
: "b" (info)
);
首先是把sysinfo系統呼叫的結構體指標引數傳入ebx暫存器,接著把系統呼叫號0x74傳入eax暫存器,接著進中斷Int $0x80,最後把系統呼叫返回結果給error變數。可以看出,沒有程式一的sysinfo(info),程式二一樣成功呼叫系統呼叫sysinfo。