DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> JavaScript入門知識 >> AJAX入門 >> AJAX詳解 >> 修改Linux內核增加系統調用
修改Linux內核增加系統調用
編輯:AJAX詳解     

修改Linux內核增加系統調用

 

本文修改內核2.4.29,分兩部分,第一部分修改內核並測試,第二部分解釋從用戶態調用新系統調用的過程。在Intel處理器上,可以通過調用門和軟中斷兩種方式實現系統調用。Linux選擇軟中斷的方式,內核通過軟中斷(int $0x80)給用戶提供服務,即系統調用。本文參考了文後給出地址的文章,甚至系統調用代碼也是從中而來,本文重在解釋其調用過程。

 

一,修改內核

增加系統調用只修改/usr/src/Linux-2.4.29/include/asm-i386/unistd.h和arch/i386/kernel/entry.S,系統調用函數一般在kernel/sys.c中,這裡把增加的系統調用代碼也加入這個文件中。

 

1.修改kernel/sys.c文件,加入自己的系統調用代碼,同參考文獻(見文後地址)中,

asmlinkage int sys_addtotal(int numdata)

{

int i=0,enddata=0;

while(i<=numdata)

enddata+=i++;

return enddata;

}

計算從0到numdata的累加值。asmlinkage表示通過堆棧遞參數。

 

2.然後把sys_addtotal(int )的入口地址添加到sys_call_table表中。該表依次存儲所有系統調用的入口地址。

修改前為:

.long SYMBOL_NAME(sys_ni_syscall)    /* sys_set_tid_address 這是第258個系統調用* /

.rept NR_syscalls-(.-sys_call_table)/4

   .long SYMBOL_NAME(sys_ni_syscall)

修改後:

.long SYMBOL_NAME(sys_ni_syscall)       /* sys_set_tid_address *        /

.long SYMBOL_NAME(sys_addtotal)          /*這是增加的第259個系統調用*/

.rept NR_syscalls-(.-sys_call_table)/4-1      /*這裡重復次數減少1*/

       .long SYMBOL_NAME(sys_ni_syscall)

 

3. 把增加的 sys_call_table 表項所對應的向量,在include/asm-i386/unistd.h 中進行必要申明,以供用戶進程和其他系統進程查詢或調用:

#define __NR_exit_group         252

#define __NR_addtotal           259   /*這是增加的第259個系統調用*/

 

然後編譯內核make bzImage,並用生成的新內核啟動系統。

 

4.測試程序(test.c)如下:

#include <stdio.h>

#include <asm/unistd.h>

int errno;

 

_syscall1(int,addtotal,int,num);//_syscall1表示該系統調用有1個參數,同樣_syscall2表示2個調用參數

 

main()

{

        int i,j;

        printf("Please input a number\n");

        while(scanf("%d",&i)==EOF);

        j=addtotal(i);

        printf("Total from 0 to %d is %d\n",i,j);

}

 

編譯:gcc -I/usr/src/Linux-2.4.29/include test.c

運行即可。

 

二,下面解釋調用系統調用的過程

測試程序test.c中的_syscall1是定義在include/asm-i386/unistd.h中的宏:

#define _syscall1(type,name,type1,arg1) \

type name(type1 arg1) \

{ \

long __res; \

__asm__ volatile ("int $0x80" \

        : "=a" (__res) \

        : "0" (__NR_##name),"b" ((long)(arg1))); \

__syscall_return(type,__res); \

}

其中__syscall_return也定義在該文件中

#define __syscall_return(type, res) \

do { \

         if ((unsigned long)(res) >= (unsigned long)(-125)) { \

                errno = -(res); \

                 res = -1; \

        } \

        return (type) (res); \

} while (0)

所以test.c中_syscall1(int,addtotal,int,num)展開後即:

int addtotal(int num)

{

long __res;

       __asm__ volatile(“int $0x80”

                                   :”=a”(__res)                                  :”0”(__NR_addtotal),”b”((long)(num)));

       do {

         if ((unsigned long)(__res) >= (unsigned long)(-125)) {

                errno = -(__res);

                 __res = -1;

        }

        return (int) (__res);

} while (0)

}

通過軟中斷int $0x80,其中系統調用號為eax中的__NR_##name,這裡也就是__NR_addtotal,在上面的步驟3中有#define __NR_addtotal 259,即259號系統調用。寄存器ebx中存第一個參數num。

 

IDT中第0x80個門(其類型為15,即陷阱門)為系統啟動(init/main.c中start_kernel調用i386/kernel/traps.c中trap_init)時設置的,trap_init中set_system_gate(0x80,&system_call);故int $0x80指令通過該系統門後轉到內核的system_call處執行。

 

system_call定義在arch/i386/kernel/entry.S中:

ENTRY(system_call)                   //轉到此處執行

        pushl %eax                      # save orig_eax

        SAVE_ALL                   //把寄存器壓入堆棧

        GET_CURRENT(%ebx)

        testb $0x02,tsk_ptrace(%ebx)    # PT_TRACESYS

        jne tracesys

        cmpl $(NR_syscalls),%eax

        jae badsys

 call *SYMBOL_NAME(sys_call_table)(,%eax,4) //此時eax=系統調用號=__NR_addtotal=259

        movl %eax,EAX(%esp)             # save the return value

ENTRY(ret_from_sys_call)          //從系統調用返回

        cli                   # need_resched and signals atomic test          cmpl $0,need_resched(%ebx)

        jne reschedule       //如果需要重新調度則跳去調度

        cmpl $0,sigpending(%ebx)

        jne signal_return

restore_all:   

        RESTORE_ALL

 

sys_call_table即第一部分2中修改的部分,可以看成一個函數指針數組,按下標指向對應系統調用的函數地址,此處即call *SYMBOL_NAME(sys_call_table)(,%eax,4)調用我們asmlinkage int sys_addtotal(int numdata)。

上面的SAVE_ALL是定義在arch/i386/kernel/entry.S中的宏:

#define SAVE_ALL \

        cld; \ 

        pushl %es; \

        pushl %ds; \

        pushl %eax; \

        pushl %ebp; \

        pushl %edi; \

        pushl %esi; \

        pushl %edx; \

        pushl %ecx; \

     

pushl %ebx; \

        movl $(__KERNEL_DS),%edx; \

        movl %edx,%ds; \

        movl %edx,%es;

所以進入系統調用函數後(本文是sys_addtotal),堆棧中參數對應SAVE_ALL中的ebx,即num。

在系統調用時,Linux在用戶空間通過寄存器而不是堆棧傳遞參數(可以從宏_systemcall0,_systemcall1,……_systemcall5看到),這些傳遞參數的寄存器是:eax系統調用號,如果有參數的話,ebx,ecx,edx,esi,edi依次存第一個到第五個參數。系統調用時,傳遞的參數最多5個。進入內核system_call後通過SAVE_ALL把寄存器壓入堆棧,系統調用函數定義時asmlinkage int sys_addtotal(int numdata)中asmlinkage表示通過堆棧傳遞參數。進入系統調用函數後看到堆棧中第一個參數即ebx,第二個參數為ecx,對應了用戶空間5個參數的順序。當然,如果系統調用定義時只有一個參數,則只會使用ebx(位於堆棧中)一個參數了。

 

系統調用返回時保存返回值在寄存器eax中。然後到ret_from_sys_call:從系統調用返回,如果需要調度或有信號待處理則去調度或處理,這超出了本文的范圍,最後返回用戶空間。

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved