Linux系统调用过程

一. 概述

Linux系统调用是应用程序与内核交互的一种方式。系统调用作为一种接口,通过系统调用,应用程序能够进入操作系统内核,从而使用内核提供的各种资源,比如操作硬件,开关中断,改变特权模式等等。首先,系统调用是一个软中断,既然是中断那么一般就具有中断号和中断处理程序两个属性,Linux使用0x80号中断作为系统调用的入口,而中断处理程序的地址放在中断向量表里。

二. 过程

基于linux-2.6.38,以read()系统调用函数为例进行说明。

在用户空间,read()函数的声明位于#include<unistd.h>,原型为:ssize_t read(int fd, void *buf, size_t count)。下面是read()函数在用户空间的定义的伪代码:

ssize_t read(int fd, void *buf, size_t count)
 {
        long res;
        %eax = __NR_read
        %ebx = fd
        %ecx = (long)buf
        %edx= count
        int $0x80
        res = %eax
        return res;
 }

第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中定义:

void __init trap_init(void)
 {
 ...................
 
    #ifdef CONFIG_X86_32
        set_system_trap_gate(SYSCALL_VECTOR, &system_call);
        set_bit(SYSCALL_VECTOR, used_vectors);
    #endif
 ...................
 }

如第6行所示。SYSCALL_VECTOR是系统调用的中断号,在/arch/x86/include/asm/irq_vectors.h中定义:

#ifdef CONFIG_X86_32
# define SYSCALL_VECTOR            0x80
#endif

正好是0x80。而system_call是系统调用的中断处理函数指针,用户执行int $0x80后会执行到这个函数,它在/arch/x86/kernel/entry_32.S中定义:

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

第4行,SAVE_ALL是一个宏,也在这个文件里定义:

.macro SAVE_ALL
     cld
     PUSH_GS
     pushl_cfi %fs
     /*CFI_REL_OFFSET fs, 0;*/
     pushl_cfi %es
     /*CFI_REL_OFFSET es, 0;*/
     pushl_cfi %ds
     /*CFI_REL_OFFSET ds, 0;*/
     pushl_cfi %eax
     CFI_REL_OFFSET eax, 0
     pushl_cfi %ebp
     CFI_REL_OFFSET ebp, 0
     pushl_cfi %edi
     CFI_REL_OFFSET edi, 0
     pushl_cfi %esi
     CFI_REL_OFFSET esi, 0
     pushl_cfi %edx
     CFI_REL_OFFSET edx, 0
     pushl_cfi %ecx
     CFI_REL_OFFSET ecx, 0
     pushl_cfi %ebx
     CFI_REL_OFFSET ebx, 0
     movl $(__USER_DS), %edx
     movl %edx, %ds
     movl %edx, %es
     movl $(__KERNEL_PERCPU), %edx
     movl %edx, %fs
     SET_KERNEL_GS %edx
 .endm

主要作用就是将各个寄存器压入栈中。

相关推荐