歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

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

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

Copyright © Linux教程網 All Rights Reserved