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