一、?什么是系統(tǒng)調(diào)用
在Linux的世界里,我們經(jīng)常會遇到系統(tǒng)調(diào)用這一術(shù)語,所謂系統(tǒng)調(diào)用,就是內(nèi)核提供的、功能十分強(qiáng)大的一系列的函數(shù)。這些系統(tǒng)調(diào)用是在內(nèi)核中實(shí)現(xiàn)的,再通過一定的方式把系統(tǒng)調(diào)用給用戶,一般都通過門(gate)陷入(trap)實(shí)現(xiàn)。系統(tǒng)調(diào)用就是用戶空間應(yīng)用程序和內(nèi)核提供的服務(wù)之間的一個(gè)接口。由于服務(wù)是在內(nèi)核中提供的,因此無法執(zhí)行直接調(diào)用;相反,您必須使用一個(gè)進(jìn)程來跨越用戶空間與內(nèi)核之間的界限。在特定架構(gòu)中實(shí)現(xiàn)此功能的方法會有所不同。因此,本文將著眼于最通用的架構(gòu)?——?i386。?
二、?系統(tǒng)調(diào)用的作用
系統(tǒng)調(diào)用在Linux系統(tǒng)中發(fā)揮著巨大的作用,如果沒有系統(tǒng)調(diào)用,那么應(yīng)用程序就失去了內(nèi)核的支持。我們在編程時(shí)用到的很多函數(shù),如fork、open等這些函數(shù)最終都是在系統(tǒng)調(diào)用里實(shí)現(xiàn)的,這里我們說到了兩個(gè)函數(shù),即fork和exit,這兩函數(shù)都是glibc中的函數(shù),但是如果我們跟蹤函數(shù)的執(zhí)行過程,看看glibc對fork和exit函數(shù)的實(shí)現(xiàn)就可以發(fā)現(xiàn)在glibc的實(shí)現(xiàn)代碼里都是采用軟中斷的方式陷入到內(nèi)核中再通過系統(tǒng)調(diào)用實(shí)現(xiàn)函數(shù)的功能的。具體過程我們在系統(tǒng)調(diào)用的實(shí)現(xiàn)過程會詳細(xì)的講到。??
由此可見,系統(tǒng)調(diào)用是用戶接口在內(nèi)核中的實(shí)現(xiàn),如果沒有系統(tǒng)調(diào)用,用戶就不能利用內(nèi)核。?
三、?系統(tǒng)調(diào)用的現(xiàn)實(shí)及調(diào)用過程??
詳細(xì)講述系統(tǒng)調(diào)用的之前也講一下Linux系統(tǒng)的一些保護(hù)機(jī)制。??
Linux系統(tǒng)在CPU的保護(hù)模式下提供了四個(gè)特權(quán)級別,目前內(nèi)核都只用到了其中的兩個(gè)特權(quán)級別,分別為“特權(quán)級0”和“特權(quán)級3”,級別0也就是我們通常所講的內(nèi)核模式,級別3也就是我們通常所講的用戶模式。劃分這兩個(gè)級別主要是對系統(tǒng)提供保護(hù)。內(nèi)核模式可以執(zhí)行一些特權(quán)指令和進(jìn)入用戶模式,而用戶模式則不能。
這里特別提出的是,內(nèi)核模式與用戶模式分別使用自己的堆棧,當(dāng)發(fā)生模式切換的時(shí)候同時(shí)要進(jìn)行堆棧的切換。每個(gè)進(jìn)程都有自己的地址空間(也稱為進(jìn)程空間),進(jìn)程的地址空間也分為兩部分:用戶空間和系統(tǒng)空間,在用戶模式下只能訪問進(jìn)程的用戶空間,在內(nèi)核模式下則可以訪問進(jìn)程的全部地址空間,這個(gè)地址空間里的地址是一個(gè)邏輯地址,通過系統(tǒng)段面式的管理機(jī)制,訪問的實(shí)際內(nèi)存要做二級地址轉(zhuǎn)換,即:邏輯地址、線性地址、物理地址。?
系統(tǒng)調(diào)用對于內(nèi)核來說就相當(dāng)于函數(shù),我們是關(guān)鍵問題是從用戶模式到內(nèi)核模式的轉(zhuǎn)換、堆棧的切換以及參數(shù)的傳遞。??
四、引起系統(tǒng)調(diào)用的兩種途徑?
(1)int $0×80 ,?老式linux內(nèi)核版本中引起系統(tǒng)調(diào)用的唯一方式?
本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/mlyy225/archive/2010/01/07/5148911.aspx
如何在Linux中添加新的系統(tǒng)調(diào)用?
系統(tǒng)調(diào)用是應(yīng)用程序和操作系統(tǒng)內(nèi)核之間的功能接口。其主要目的是使得用戶可以使用操作系統(tǒng)提供的有關(guān)設(shè)備管理、輸入/輸入系統(tǒng)、文件系統(tǒng)和進(jìn)程控制、通信以及存儲管理等方面的功能,而不必了解系統(tǒng)程序的內(nèi)部結(jié)構(gòu)和有關(guān)硬件細(xì)節(jié),從而起到減輕用戶負(fù)擔(dān)和保護(hù)系統(tǒng)以及提高資源利用率的作用。?
Linux操作系統(tǒng)作為自由軟件的代表,它優(yōu)良的性能使得它的應(yīng)用日益廣泛,不僅得到專業(yè)人士的肯定,而且商業(yè)化的應(yīng)用也是如火如荼。在Linux中,大部分的系統(tǒng)調(diào)用包含在Linux的libc庫中,通過標(biāo)準(zhǔn)的C函數(shù)調(diào)用方法可以調(diào)用這些系統(tǒng)調(diào)用。那么,對Linux的發(fā)燒友來說,如何在Linux中增加新的系統(tǒng)調(diào)用呢??
1 Linux系統(tǒng)調(diào)用機(jī)制?
在Linux系統(tǒng)中,系統(tǒng)調(diào)用是作為一種異常類型實(shí)現(xiàn)的。它將執(zhí)行相應(yīng)的機(jī)器代碼指令來產(chǎn)生異常信號。產(chǎn)生中斷或異常的重要效果是系統(tǒng)自動將用戶態(tài)切換為核心態(tài)來對它進(jìn)行處理。這就是說,執(zhí)行系統(tǒng)調(diào)用異常指令時(shí),自動地將系統(tǒng)切換為核心態(tài),并安排異常處理程序的執(zhí)行。?
Linux用來實(shí)現(xiàn)系統(tǒng)調(diào)用異常的實(shí)際指令是:?
Int x80?
這一指令使用中斷/異常向量號128(即16進(jìn)制的80)將控制權(quán)轉(zhuǎn)移給內(nèi)核。為達(dá)到在使用系統(tǒng)調(diào)用時(shí)不必用機(jī)器指令編程,在標(biāo)準(zhǔn)的C語言庫中為每一系統(tǒng)調(diào)用提供了一段短的子程序,完成機(jī)器代碼的編程工作。事實(shí)上,機(jī)器代碼段非常簡短。它所要做的工作只是將送給系統(tǒng)調(diào)用的參數(shù)加載到CPU寄存器中,接著執(zhí)行int x80指令。然后運(yùn)行系統(tǒng)調(diào)用,系統(tǒng)調(diào)用的返回值將送入CPU的一個(gè)寄存器中,標(biāo)準(zhǔn)的庫子程序取得這一返回值,并將它送回用戶程序。?
為使系統(tǒng)調(diào)用的執(zhí)行成為一項(xiàng)簡單的任務(wù),Linux提供了一組預(yù)處理宏指令。?
它們可以用在程序中。這些宏指令取一定的參數(shù),然后擴(kuò)展為調(diào)用指定的系統(tǒng)調(diào)用的函數(shù)。?
這些宏指令具有類似下面的名稱格式:?
_syscallN(parameters)?
其中N是系統(tǒng)調(diào)用所需的參數(shù)數(shù)目,而parameters則用一組參數(shù)代替。這些參數(shù)使宏指令完成適合于特定的系統(tǒng)調(diào)用的擴(kuò)展。例如,為了建立調(diào)用setuid()系統(tǒng)調(diào)用的函數(shù),應(yīng)該使用:?
_syscall1(?int,?setuid,?uid_t,?uid?)?
syscallN(?)宏指令的第1個(gè)參數(shù)int說明產(chǎn)生的函數(shù)的返回值的類型是整型,第2個(gè)參數(shù)setuid說明產(chǎn)生的函數(shù)的名稱。后面是系統(tǒng)調(diào)用所需要的每個(gè)參數(shù)。這一宏指令后面還有兩個(gè)參數(shù)uid_t和uid分別用來指定參數(shù)的類型和名稱。?
另外,用作系統(tǒng)調(diào)用的參數(shù)的數(shù)據(jù)類型有一個(gè)限制,它們的容量不能超過四個(gè)字節(jié)。這是因?yàn)閳?zhí)行int ?{GetProperty(Content)}x80指令進(jìn)行系統(tǒng)調(diào)用時(shí),所有的參數(shù)值都存在32位的CPU寄存器中。使用CPU寄存器傳遞參數(shù)帶來的另一個(gè)限制是可以傳送給系統(tǒng)調(diào)用的參數(shù)的數(shù)目。這個(gè)限制是最多可以傳遞5個(gè)參數(shù)。所以Linux一共定義了6個(gè)不同的_syscallN()宏指令,從_syscall0()、_syscall1()直到_syscall5()。?
一旦_syscallN()宏指令用特定系統(tǒng)調(diào)用的相應(yīng)參數(shù)進(jìn)行了擴(kuò)展,得到的結(jié)果是一個(gè)與系統(tǒng)調(diào)用同名的函數(shù),它可以在用戶程序中執(zhí)行這一系統(tǒng)調(diào)用。?
2?添加新的系統(tǒng)調(diào)用?
如果用戶在Linux中添加新的系統(tǒng)調(diào)用,應(yīng)該遵循幾個(gè)步驟才能添加成功,下面幾個(gè)步驟詳細(xì)說明了添加系統(tǒng)調(diào)用的相關(guān)內(nèi)容。?
(1)?添加源代碼?
第一個(gè)任務(wù)是編寫加到內(nèi)核中的源程序,即將要加到一個(gè)內(nèi)核文件中去的一個(gè)函數(shù),該函數(shù)的名稱應(yīng)該是新的系統(tǒng)調(diào)用名稱前面加上sys_標(biāo)志。假設(shè)新加的系統(tǒng)調(diào)用為mycall(int number),在/usr/src/linux/kernel/sys.c文件中添加源代碼,如下所示:?
asmlinkage int sys_mycall(int number)?
{?
return number;?
}?
作為一個(gè)最簡單的例子,我們新加的系統(tǒng)調(diào)用僅僅返回一個(gè)整型值。?
(2)?連接新的系統(tǒng)調(diào)用?
添加新的系統(tǒng)調(diào)用后,下一個(gè)任務(wù)是使Linux內(nèi)核的其余部分知道該程序的存在。為了從已有的內(nèi)核程序中增加到新的函數(shù)的連接,需要編輯兩個(gè)文件。?
在我們所用的Linux內(nèi)核版本(RedHat 6.0,內(nèi)核為2.2.5-15)中,第一個(gè)要修改的文件是:?
/usr/src/linux/include/asm-i386/unistd.h?
該文件中包含了系統(tǒng)調(diào)用清單,用來給每個(gè)系統(tǒng)調(diào)用分配一個(gè)唯一的號碼。文件中每一行的格式如下:?
#define __NR_name NNN?
其中,name用系統(tǒng)調(diào)用名稱代替,而NNN則是該系統(tǒng)調(diào)用對應(yīng)的號碼。應(yīng)該將新的系統(tǒng)調(diào)用名稱加到清單的最后,并給它分配號碼序列中下一個(gè)可用的系統(tǒng)調(diào)用號。我們的系統(tǒng)調(diào)用如下:?
#define __NR_mycall 191?
系統(tǒng)調(diào)用號為191,之所以系統(tǒng)調(diào)用號是191,是因?yàn)長inux-2.2內(nèi)核自身的系統(tǒng)調(diào)用號碼已經(jīng)用到190。?
第二個(gè)要修改的文件是:?
/usr/src/linux/arch/i386/kernel/entry.S?
該文件中有類似如下的清單:?
.long SYMBOL_NAME()?
該清單用來對sys_call_table[]數(shù)組進(jìn)行初始化。該數(shù)組包含指向內(nèi)核中每個(gè)系統(tǒng)調(diào)用的指針。這樣就在數(shù)組中增加了新的內(nèi)核函數(shù)的指針。我們在清單最后添加一行:?
.long SYMBOL_NAME(sys_mycall)?
(3)?重建新的Linux內(nèi)核?
為使新的系統(tǒng)調(diào)用生效,需要重建Linux的內(nèi)核。這需要以超級用戶身份登錄。?
#pwd?
/usr/src/linux?
#?
超級用戶在當(dāng)前工作目錄(/usr/src/linux)下,才可以重建內(nèi)核。?
#make config?
#make dep?
#make clearn?
#make bzImage?
編譯完畢后,系統(tǒng)生成一可用于安裝的、壓縮的內(nèi)核映象文件:?
/usr/src/linux/arch/i386/boot/bzImage?
(4)?用新的內(nèi)核啟動系統(tǒng)?
要使用新的系統(tǒng)調(diào)用,需要用重建的新內(nèi)核重新引導(dǎo)系統(tǒng)。為此,需要修改/etc/lilo.conf文件,在我們的系統(tǒng)中,該文件內(nèi)容如下:?
boot=/dev/hda?
map=/boot/map?
install=/boot/boot.b?
prompt?
timeout=50?
image=/boot/vmlinuz-2.2.5-15?
label=linux?
root=/dev/hdb1?
read-only?
other=/dev/hda1?
label=dos?
table=/dev/had?
首先編輯該文件,添加新的引導(dǎo)內(nèi)核:?
image=/boot/bzImage-new?
label=linux-new?
root=/dev/hdb1?
read-only?
添加完畢,該文件內(nèi)容如下所示:?
boot=/dev/hda?
map=/boot/map?
install=/boot/boot.b?
prompt?
timeout=50?
image=/boot/bzImage-new?
label=linux-new?
root=/dev/hdb1?
read-only?
image=/boot/vmlinuz-2.2.5-15?
label=linux?
root=/dev/hdb1?
read-only?
other=/dev/hda1?
label=dos?
table=/dev/hda?
這樣,新的內(nèi)核映象bzImage-new成為缺省的引導(dǎo)內(nèi)核。?
為了使用新的lilo.conf配置文件,還應(yīng)執(zhí)行下面的命令:?
#cp /usr/src/linux/arch/i386/boot/zImage /boot/bzImage-new?
其次配置lilo:?
# /sbin/lilo?
現(xiàn)在,當(dāng)重新引導(dǎo)系統(tǒng)時(shí),在boot:提示符后面有三種選擇:linux-new?、linux、dos,新內(nèi)核成為缺省的引導(dǎo)內(nèi)核。?
至此,新的Linux內(nèi)核已經(jīng)建立,新添加的系統(tǒng)調(diào)用已成為操作系統(tǒng)的一部分,重新啟動Linux,用戶就可以在應(yīng)用程序中使用該系統(tǒng)調(diào)用了。?
(5)使用新的系統(tǒng)調(diào)用?
在應(yīng)用程序中使用新添加的系統(tǒng)調(diào)用mycall。同樣為實(shí)驗(yàn)?zāi)康?,我們寫了一個(gè)簡單的例子xtdy.c。?
#include?
_syscall1(int,mycall,int,ret)?
main()?
{?
printf("%d n",mycall(100));?
}?
編譯該程序:?
# cc -o xtdy xtdy.c?
執(zhí)行:?
# xtdy?
結(jié)果:?
# 100?
注意,由于使用了系統(tǒng)調(diào)用,編譯和執(zhí)行程序時(shí),用戶都應(yīng)該是超級用戶身份
http://blog.chinaunix.net/u2/62213/showart_488383.html
linux?添加系統(tǒng)調(diào)用
一.源碼修改
1下載一個(gè)與所用系統(tǒng)內(nèi)核版本接近的內(nèi)核,放在/usr/src下,解壓,作個(gè)鏈接ln -s linux-2.6.18.1 linux
2修改:修改三個(gè)地方
1)/usr/src/linux/kerner/sys.c中添加,
asmlinkage int sys_mysyscall(int a)
{
return a;
}
2)定義系統(tǒng)調(diào)用號,/usr/src/linux/include/asm-i386/unistd.h
#define _NR_sysmycall 318 //不能與前面已有的重復(fù)
#define _NR_syscalls??319//修改系統(tǒng)中所用系統(tǒng)調(diào)用數(shù)目?
3)在系統(tǒng)調(diào)用向量表里添加自定義的系統(tǒng)調(diào)用函數(shù)入口位置,
/usr/src/linux/arch/i386/kernel/syscall_table.S,以前老版本是entry.s
.long sys_mysyscall
二、內(nèi)核編譯
1.在/boot下復(fù)制配置文件,到/usr/src/linux下,改名位config,make menuconfig,可以不用修改,直接退出
2.make clean?清空以前的編譯痕跡
3.make,編譯出來的是bzImage
4.make modules,make modules-install//編譯、安裝config里配置的模塊
如不執(zhí)行次步驟,對于有的系統(tǒng),制作不了initrd文件。系統(tǒng)就啟動不了
5.如果直接make install,系統(tǒng)會自動制作initrd文件,并復(fù)制initrd和bzimage文件到/boot下,修改grub.conf文件,重啟系統(tǒng),選擇進(jìn)入新內(nèi)核
6.不使用make install命令。復(fù)制bzImage到/boot下,改名位vmlinuz-2.6.18.1,手工制作initrd文件,/mkinitrd initrd-2.6.18.1.img 2.6.18.1,initrd文件名位initrd-2.6.18.1
7.修改grub.conf文件,復(fù)制原來已有的啟動設(shè)置,把title和kernel和initrd改名為新制作的即可
三、編寫代碼測試
int main(void)
{
int a=syscall(318,100);//318是系統(tǒng)調(diào)用號,100是參數(shù)
printf("%d\n",a);
return 0;
}
syscall是內(nèi)核提供為用戶程序的一個(gè)函數(shù),
如果不使用syscall函數(shù),也可以使用宏定義,但是在2.6.20以后的版本里,沒有宏定義,需要自己從其他版本里復(fù)制過來添加。
?
評論
查看更多