您的位置:首页 > 其它

分析system_call中断处理过程

2016-12-24 21:54 417 查看


转自:http://tsengyia.blog.chinaunix.net/uid-30156195-id-4938101.html


赵建清+原创作品转载请注明出处+《Linux内核分析》MOOC课程http://mooc.study.163.com/learn/USTC-1000029000

概述

在Linux系统中应用程序发起系统调用后,使用int $0x80或sysenter汇编指令将CPU切换到内核态,然后开始从地址system_call处执行命令。

代码分析

IA-32体系结构下system_call的定义位于内核代码arch/x86/kernel/entry_32.S文件中,由文件名即可知道system_call为汇编代码。下面附上linux-3.18.6内核版本中system_call的代码:
ENTRY(system_call)

    RING0_INT_FRAME                       # can't unwind into user space anyway

    ASM_CLAC
    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)
syscall_after_call:

    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:

#ifdef CONFIG_X86_ESPFIX32
    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

#endif
restore_nocheck:

    RESTORE_REGS 4                              # skip orig_eax/error_code

irq_return:

    INTERRUPT_RETURN
RING0_INT_FRAME的定义也在相同的文件中,用于设置堆栈指针寄存器esp和指令指针寄存器eip,使其指向内核:

.macro RING0_INT_FRAME

   CFI_STARTPROC simple

   CFI_SIGNAL_FRAME

   CFI_DEF_CFA esp, 3*4

   /*CFI_OFFSET cs, -2*4;*/

   CFI_OFFSET eip, -3*4

.endm

第4行将通用寄存器eax入栈,用于保存系统调用号。

第5行宏SAVE_ALL在栈中保存中断处理程序可能会使用到的所有CPU寄存器,但不包括eflags、cs、eip、ss和esp:

.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

第6行ebp用于存放当前进程thread_info结构的地址。

第8行检查是否有某一调试程序正在跟踪执行程序对系统调用的调用,如果需要追踪当前执行程序,则调转到syscall_trace_entry,调试进程会收集必要的信息。

第9行对用户态进程传来的系统调用号进行有效性检查。如果这个系统调用号不在有效范围内,系统调用处理程序就终止,并在eax中设置错误返回码。

第12行调用与eax中所包含的系统调用号对应的特定服务例程,因为分派表中的每个表项占4个字节,因此首先把系统调用号乘以4,再加上sys_call_table分派表的起始地址,内核就找到了要调用的服务例程。

第18行和第21行关闭本地中断并检查当前进程的thread_info结构中的标志。如果所有的标志没有被设置则跳转到restore_all标记处。

第29行到第35行用于恢复保存在内核栈中的寄存器的值。

第44行执行iret汇编指令重新开始执行用户态进程。

总结

system_call是发起系统调用的必经之路,在执行之前CPU处于用户态,执行system_call过程中系统进入内核态,执行系统调用服务例程,system_call

返回后又切换回用户态。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: