一. 概述
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
主要作用就是將各個寄存器壓入棧中。