分类:
2011-04-16 13:57:19
注:本文涉及的硬件平台默认为x86
系统调用是用户态访问进入到内核态从而可以访问内核资源的一个桥梁。比如说在用户态可以调用getpid()这个系统调用来获取当前进程号,当前进程号是存在内核的task_struct结构体中的,只有内核态才能访问到。如果用户态试图访问内核地址空间将会产生一个段错误。
首先内核需要实现好相应的系统调用号的处理函数。
在linux-syscall.h定义系统调用号
#define __NR_getpid (__NR_SYSCALL_BASE + 20)
在syscalls.txt生命要生成代码的系统调用汇编文件
# each non comment line has the following format:
# return_type func_name[:syscall_name[:call_id]]([parameter_list]) (#syscall_number|stub)
pid_t getpid () 20
通过gensyscalls.py产生汇编文件getpid.S,内容如下:
/* autogenerated by gensyscalls.py */ #include .text .type getpid, @function .globl getpid .align 4 getpid: movl $__NR_getpid, %eax //将系统调用好保存到eax寄存器 int $0x80 //编程异常。陷入到内核态 cmpl $-129, %eax
jb negl %eax pushl %eax call __set_errno addl $4, %esp orl $-1, %eax 1: ret |
在用户态执行了int $0x80之后,cpu产生了一个编程异常。Cpu的控制单元做了一系列的硬件处理之后,比如保存eflags,cs,eip,ss,esp寄存器,并载入新的值切换到内核态。然后cpu跳转到0x80相应的中断处理函数处执行。(硬件处理流程具体见深入理解linux内核的中断和异常的硬件处理)
内核的中断处理函数是:system_call
ENTRY(system_call) RING0_INT_FRAME # can't unwind into user space anyway pushl %eax # save orig_eax CFI_ADJUST_CFA_OFFSET 4 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 syscall_exit: #退出系统调用 LOCKDEP_SYS_EXIT DISABLE_INTERRUPTS(CLBR_ANY) # make sure we don't miss an interrupt # setting need_resched or sigpending # between sampling and the iret TRACE_IRQS_OFF movl TI_flags(%ebp), %ecx testl $_TIF_ALLWORK_MASK, %ecx # current->work jne syscall_exit_work restore_all: TRACE_IRQS_IRET restore_all_notrace: movl PT_EFLAGS(%esp), %eax # mix EFLAGS, SS and CS # Warning: PT_OLDSS(%esp) contains the wrong/random values if we # are returning to the kernel. # See comments in process.c:copy_thread() for details. movb PT_OLDSS(%esp), %ah movb PT_CS(%esp), %al andl $(X86_EFLAGS_VM | (SEGMENT_TI_MASK << 8) | SEGMENT_RPL_MASK), %eax cmpl $((SEGMENT_LDT << 8) | USER_RPL), %eax CFI_REMEMBER_STATE je ldt_ss # returning to user-space with LDT SS restore_nocheck: RESTORE_REGS 4 # skip orig_eax/error_code CFI_ADJUST_CFA_OFFSET -4 irq_return: INTERRUPT_RETURN |