Linux系統調用過程

來源:互聯網
上載者:User

一. 概述

         系統調用是應用程式與核心互動的一種方式。系統調用作為一種介面,通過系統調用,應用程式能夠進入作業系統核心,從而使用核心提供的各種資源,比如操作硬體,開關中斷,改變特權模式等等。首先,系統調用是一個非強制中斷,既然是中斷那麼一般就具有中斷號和中斷處理常式兩個屬性,Linux使用0x80號中斷作為系統調用的入口,而中斷處理常式的地址放在中斷向量表裡。

 

二. 過程

         基於linux-2.6.38,以read()系統調用函數為例進行說明。    

       在使用者空間,read()函數的聲明位於#include<unistd.h>,原型為:ssize_t read(int fd, void *buf, size_t count)。下面是read()函數在使用者空間的定義的虛擬碼:

 1 ssize_t read(int fd, void *buf, size_t count) 2 { 3        long res; 4        %eax = __NR_read 5        %ebx = fd  6        %ecx = (long)buf 7        %edx= count 8        int $0x80 9        res = %eax10        return res;11 }

         第4行,用eax寄存器儲存read()的系統調用號,在/arch/x86/include/asm/unistd_32.h裡定義(#define __NR_read  3);第5~7行,分別將三個參數放入三個寄存器(通過寄存器來傳遞參數);第8行,執行系統調用,進入核心;第9行,擷取eax寄存器所儲存的函數傳回值。

       執行第8行後已經進入了系統核心,由於這是一個中斷,因此程式進入到中斷向量表中記錄0x80號的中斷處理常式,中斷向量表的初始化在/arch/x86/kernel/traps.c中定義:

 1 void __init trap_init(void) 2 { 3 ................... 4  5    #ifdef CONFIG_X86_32 6        set_system_trap_gate(SYSCALL_VECTOR, &system_call); 7        set_bit(SYSCALL_VECTOR, used_vectors); 8    #endif 9 ...................10 }

如第6行所示。SYSCALL_VECTOR是系統調用的中斷號,在/arch/x86/include/asm/irq_vectors.h中定義:

1 #ifdef CONFIG_X86_322 # define SYSCALL_VECTOR            0x803 #endif

正好是0x80。而system_call是系統調用的中斷處理函數指標,使用者執行int $0x80後會執行到這個函數,它在/arch/x86/kernel/entry_32.S中定義:

 1 ENTRY(system_call) 2     RING0_INT_FRAME            # can't unwind into user space anyway 3     pushl_cfi %eax            # save orig_eax 4     SAVE_ALL 5     GET_THREAD_INFO(%ebp) 6                     # system call tracing in operation / emulation 7     testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp) 8     jnz syscall_trace_entry 9     cmpl $(nr_syscalls), %eax10     jae syscall_badsys11 syscall_call:12     call *sys_call_table(,%eax,4)13     movl %eax,PT_EAX(%esp)        # store the return value
...........

第4行,SAVE_ALL是一個宏,也在這個檔案裡定義:

 1 .macro SAVE_ALL 2     cld 3     PUSH_GS 4     pushl_cfi %fs 5     /*CFI_REL_OFFSET fs, 0;*/ 6     pushl_cfi %es 7     /*CFI_REL_OFFSET es, 0;*/ 8     pushl_cfi %ds 9     /*CFI_REL_OFFSET ds, 0;*/10     pushl_cfi %eax11     CFI_REL_OFFSET eax, 012     pushl_cfi %ebp13     CFI_REL_OFFSET ebp, 014     pushl_cfi %edi15     CFI_REL_OFFSET edi, 016     pushl_cfi %esi17     CFI_REL_OFFSET esi, 018     pushl_cfi %edx19     CFI_REL_OFFSET edx, 020     pushl_cfi %ecx21     CFI_REL_OFFSET ecx, 022     pushl_cfi %ebx23     CFI_REL_OFFSET ebx, 024     movl $(__USER_DS), %edx25     movl %edx, %ds26     movl %edx, %es27     movl $(__KERNEL_PERCPU), %edx28     movl %edx, %fs29     SET_KERNEL_GS %edx30 .endm

主要作用就是將各個寄存器壓入棧中。

第9行,比較eax的值是否大於等於nr_syscalls,nr_syscalls是比最大有效系統調用號大1的值,在/arch/x86/kernel/entry_32.S中定義:

1 #define nr_syscalls ((syscall_table_size)/4)

其中syscall_table_size就是系統調用表的大小(單位:位元組),syscall_table_size其實是一個數組,數組裡存放的是各個系統調用函數的地址,元素類型是long型,除以4剛好是系統調用函數的個數。

如果從eax寄存器傳進來的系統調用號有效,那麼就執行第12行,在系統調用表裡找到相應的系統調用服務程式,sys_call_table在/arch/x86/kernel/syscall_table_32.S中定義:

1 ENTRY(sys_call_table)2     .long sys_restart_syscall    /* 0 - old "setup()" system call, used for restarting */3     .long sys_exit4     .long ptregs_fork5     .long sys_read6     .long sys_write7     .long sys_open        /* 5 */8     .long sys_close9 .................
*sys_call_table(,%eax,4)指的是sys_call_table裡位移量為%eax*4上的那個值指向的函數,這裡%eax=3,那麼第5行的sys_read()函數就會被調用。sys_read()在/fs/read_write.c中定義:
 1 SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count) 2 {        3         struct file *file;       4         ssize_t ret = -EBADF; 5         int fput_needed; 6          7         file = fget_light(fd, &fput_needed); 8         if (file) { 9                 loff_t pos = file_pos_read(file);10                 ret = vfs_read(file, buf, count, &pos);11                 file_pos_write(file, pos);12                 fput_light(file, fput_needed);13         }14         15         return ret;16 } 

可見,參數的形式和使用者空間的一樣。SYSCALL_DEFINE3是一個宏,在/include/linux/syscalls.h中定義:

1 #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)

SYSCALL_DEFINEx也是一個宏,也在此檔案中定義:

1 #define SYSCALL_DEFINEx(x, sname, ...)                \2     __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)3 .....4 #define __SYSCALL_DEFINEx(x, name, ...)                    \5     asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))6 ......

宏展開後,就是聲明了這麼一個函數:

asmlinkage long sys_read(unsigned int fd, char __user *buf, size_t count);

asmlingage是一個宏,定義為:__attribute__((regparm(0))),作用是讓這個函數只從棧上擷取參數(因為之前的SAVE_ALL將參數壓到了棧裡面)。

      當執行完中斷處理常式後,後面會調用RESTORE_REGS來恢複各個寄存器:

1 ..............2     CFI_REMEMBER_STATE3     je ldt_ss            # returning to user-space with LDT SS4 restore_nocheck:5     RESTORE_REGS 4            # skip orig_eax/error_code6 ...............

第5行,RESTORE_REGS的定義:

 1 .macro RESTORE_REGS pop=0 2     RESTORE_INT_REGS 3 1:    popl_cfi %ds 4     /*CFI_RESTORE ds;*/ 5 2:    popl_cfi %es 6     /*CFI_RESTORE es;*/ 7 3:    popl_cfi %fs 8     /*CFI_RESTORE fs;*/ 9     POP_GS \pop10 .................

第2行,RESTORE_INT_REGS的定義:

 1 .macro RESTORE_INT_REGS 2     popl_cfi %ebx 3     CFI_RESTORE ebx 4     popl_cfi %ecx 5     CFI_RESTORE ecx 6     popl_cfi %edx 7     CFI_RESTORE edx 8     popl_cfi %esi 9     CFI_RESTORE esi10     popl_cfi %edi11     CFI_RESTORE edi12     popl_cfi %ebp13     CFI_RESTORE ebp14     popl_cfi %eax15     CFI_RESTORE eax16 .endm

       到這裡差不多了,再對read()跟蹤的話就會涉及檔案系統方面的內容,以後會說的。

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.