1. 程式人生 > >Linux核心分析之四——系統呼叫的工作機制

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。