首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 操作系统 > UNIXLINUX >

linux内核-系统调用(4)

2013-10-02 
linux内核--系统调用(四)一. 概述系统调用是应用程序与内核交互的一种方式。系统调用作为一种接口,通过系统

linux内核--系统调用(四)

一. 概述

         系统调用是应用程序与内核交互的一种方式。系统调用作为一种接口,通过系统调用,应用程序能够进入操作系统内核,从而使用内核提供的各种资源,比如操作硬件,开关中断,改变特权模式等等。首先,系统调用是一个软中断,既然是中断那么一般就具有中断号和中断处理程序两个属性,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 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 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 .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()跟踪的话就会涉及文件系统方面的内容,以后会说的

热点排行