分析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返回后又切换回用户态。
相关文章推荐
- 实验五:分析system_call中断处理过程
- 通过实验分析system_call中断处理过程
- 通过分析system_call中断处理过程来深入理解系统调用
- 《Linux内核分析》第五周:分析system_call中断处理过程
- Linux内核分析5:分析system_call中断处理过程
- 分析system_call中断处理过程
- 分析system_call中断处理过程
- MOOC《Linux内核分析》——分析system_call中断处理过程
- 分析system_call中断处理过程
- 魏昊卿——第五周作业:分析system_call中断处理过程
- Linux内核分析课程5_system_call中断处理过程
- MOOC-Linux内核lab5 分析system_call中断处理过程
- system_call中断处理过程分析
- Linux内核分析——分析system_call中断处理过程
- system_call中断处理过程分析
- 分析system_call中断处理过程
- 作业五:分析system_call中断处理过程
- 通过实验分析system_call中断处理过程
- 分析system_call中断处理过程
- 分析system_call中断处理过程