Linux用來實現系統調用異常的實際指令是:
Int ?$0x80
這一指令使用中斷/異常向量號128(即16進制的80)將控制權轉移給內核。為達到在使用系統調用時不必用機器指令編程,在標準的C語言庫中為每一系統調用提供了一段短的子程序,完成機器代碼的編程工作。事實上,機器代碼段非常簡短。它所要做的工作只是將送給系統調用的參數載入到CPU寄存器中,接著執行int ?$0x80指令。然後運行系統調用,系統調用的返回值將送入CPU的一個寄存器中,標準的庫子程序取得這一返回值,並將它送回用戶程序。
為使系統調用的執行成為一項簡單的任務,Linux提供了一組預處理宏指令。
它們可以用在程序中。這些宏指令取一定的參數,然後擴展為調用指定的系統調用的函數。
這些宏指令具有類似下面的名稱格式:
_syscallN(parameters)
其中N是系統調用所需的參數數目,而parameters則用一組參數代替。這些參數使宏指令完成適合於特定的系統調用的擴展。例如,為了建立調用setuid()系統調用的函數,應該使用:
_syscall1( int, setuid, uid_t, uid )
syscallN( )宏指令的第1個參數int說明產生的函數的返回值的類型是整型,第2個參數setuid說明產生的函數的名稱。後面是系統調用所需要的每個參數。這一宏指令後面還有兩個參數uid_t和uid分別用來指定參數的類型和名稱。
另外,用作系統調用的參數的數據類型有一個限制,它們的容量不能超過四個位元組。這是因為執行int
?$0x80指令進行系統調用時,所有的參數值都存在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)
❷ 如何實現一個新的linux系統調用
若要在 kernel 裡面新增加一來個自己的 sys call,大源致需要這么幾個步驟: a,新增自己 sys call 的代碼,並修改相應 makefile; b,修改相應頭文件,分配自己的系統調用號; c,系統調用通過中斷加查表的方式實現,
❸ 如何在Linux內核里增加一個系統調用
一、Linux0.11下添加系統調用:x0dx0ax0dx0a我在bochs2.2.1中對linux0.11內核添加了一個新的系統調用,步驟如下: x0dx0a1./usr/src/linux/include/unistd.h中添加:#define __NR_mytest 87 x0dx0a然後在下面聲明函數原型:int mytest(); x0dx0a2./usr/src/linux/include/linux/sys.h中添加:extern int sys_mytest(); x0dx0a然後在sys_call_table中最後加上sys_mytest; x0dx0a3.在/usr/src/linux/kernel/sys.c中添加函數實現如下: x0dx0aint sys_mytest(){ x0dx0aprintk("This is a test!"); x0dx0areturn 123; x0dx0a} x0dx0a4.在/usr/src/linux/kernel/system_call.s中對系統調用號加1(原來是86改成了87) x0dx0a5.然後到/usr/src/linux目錄下編譯內核make clean; make Image x0dx0a6. cp /usr/src/linux/include/unistd.h /usr/include/unistd.h x0dx0a7. reset bochs x0dx0a8. 在/usr/root中生成test.c文件如下: x0dx0a#define __LIBRARY__ x0dx0a#include
❹ 在Linux中產生一個系統調用以及怎樣通過往Linux內核中增加一個新函數從而在該內核
若要在kernel裡面新增加一個自己的syscall,大致需要這么幾個步驟:
a,新增自己syscall的代碼,並修改相應makefile;
b,修改相應頭文件,分配自己的系統調用號;
c,系統調用通過中斷加查表的方式實現,所以需要在系統調用表裡面增加相關的項目,這需要修改相關的匯編源文件;
d,重新編譯內核,並寫測試程序測試新增的syscall;
注意幾點:
1,上述步驟所要修改的文件/位置等對不同處理器架構來說,不盡相同;
2,對於應用層的測試代碼來說,在2.6.20版本之前,可以使用相應的_syscallN宏;
但是2.6.20版本後,這些宏已經被從kernel中去掉了,你需要使用應用層的syscall函數
來測試。
你可以在自己的syscall實現文件中加上EXPORT_SYMBOL/EXPORT_SYMBOL_GPL
等宏來導出你的全局變數或者函數,導出後,另外的模塊或者其他內核代碼就可以使用
這些變數和函數。
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
杭州巨立安(JulianTec)是杭州市場上
唯一由資深研發工程師所創辦的機構。所以:
就專業程度和實在程度而言,杭州巨立安(JulianTec)
是您在arm架構下學習嵌入式Linux研發的上佳指導!
❺ linux操作系統中 使用系統調用的一般方式是怎樣的
給個例子給你看看吧,這里是linux中一個C語言程序,他用到了linux提供的系統調用,很長的程序了,或許你沒耐心看,我在最後給你列出那些地方用了系統調用
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define BUFF_LEN 1024
#define RET_ERROR 1
#define RET_OK 0
typedef struct msg_send_struct { //這就是一個消息的數據結構
long my_type; //我們就是根據這個欄位來區分每塊消息的
char my_text[BUFF_LEN];
} msg_send_struct;
int main() {
char * path = "/";
int i_porject_id = 7;
key_t key;
msg_send_struct msg_send; //定義發送消息
int i_ret;
int i_msg_id;
int i_flag = 0666|IPC_CREAT; //為消息管道的創建指定參數,IPC_CREAT表示這個消息隊列是創建,而不是搜索已經存在的消息隊列
key = ftok(path, i_porject_id);//為消息隊列生成一個key,當然你也可以手動指定,當你運氣很好沒有和已經竄在的消息隊列的key起沖突的時候
if(key == 1) {
printf("building key error\n");
exit(1);
}
i_msg_id = msgget(key, i_flag);//根據你的參數決定是創建還是搜索KEY值得消息隊列
if(i_msg_id == -1) {
printf("create msg queue error\n");
exit(1);
}
printf("i_msg_id = %d\n", i_msg_id);
msg_send.my_type = 1;
strcpy(msg_send.my_text, "hello world"); //初始化消息
i_ret = msgsnd(i_msg_id, &msg_send, strlen("hello world") + 1, IPC_NOWAIT);//開始發送,nowait表示如果隊列中消息滿了當前進程不等待直接返回錯誤,反之很容易理解吧
if(i_ret == -1) {
printf("msg send error\n");
exit(1);
}
exit(0);
}
下面是系統調用:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>這些頭文件可不是庫函數,他裡面就是linux提供的系統調用。
key = ftok(path, i_porject_id);//為消息隊列生成一個key,當然你也可以手動指定,當你運氣很好沒有和已經竄在的消息隊列的key起沖突的時候
linux中系統調用,利用文件系統和ID來創建KEY。
i_msg_id = msgget(key, i_flag);//根據i_flag值決定是創建還是尋找消息隊列的系統調用。
i_ret = msgsnd(i_msg_id, &msg_send, strlen("hello world") + 1, IPC_NOWAIT);//發送消息的系統調用msgsnd函數。
這里涉及到進程通信中的消息隊列內容,如果不明白沒什麼關系,可以看出來他和C的庫函數調用一模一樣,只不過實現方式,這需要你的知識積累到一定程度,有很大差別。對於一個程序員來說,我們看不出什麼他們和庫函數有什麼區別,這算是一種對我們的透明性。
補充:這個例子是我寫來學習進程通信內容的,由於採用了linux系統調用,所以只能在linux下面運行,還有就是我沒有考慮許可權問題,所以要編譯請用超級用戶root,由於消息隊列的特性,這個程序沒有釋放隊列(我把釋放代碼寫在了接受消息的程序中),第2次運行就會報消息隊列不能創建的錯誤。