有些工作如果只是在用户空间是没有办法完成的,比如取得时间、操作文件等,这个时候就需要系统调用了。而一般的系统调用的过程是:我们的程序调用C库包装好的一些函数,这些函数去执行系统调用,然后再把返回的结果给我们看。现在有一个问题就出现了:用户空间是不能去访问内存空间的,那么怎么去运行内核空间中的代码呢?具体的过程如下:
首先程序触发int 0x80软中断陷入内核态。
然后系统跳到一个预设的内核地址,该位置指向了系统调用处理程序system_call,下面是该函数的代码:
ENTRY(system_call)
RING0_INT_FRAME
pushl %eax
CFI_ADJUST_CFA_OFFSET 4
SAVE_ALL
#ifdef CONFIG_EVENT_TRACE
pushl %edx; pushl %ecx; pushl %ebx; pushl %eax
call sys_call
popl %eax; popl %ebx; popl %ecx; popl %edx
#endif
GET_THREAD_INFO(%ebp)
testw $(_TIF_SYSCALL_EMU|_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT),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)
syscall_exit:
LOCKDEP_SYS_EXIT
DISABLE_INTERRUPTS(CLBR_ANY)
TRACE_IRQS_OFF
testl $TF_MASK,PT_EFLAGS(%esp)
jz no_singlestep
orl $_TIF_SINGLESTEP,TI_flags(%ebp)
no_singlestep:
movl TI_flags(%ebp), %ecx
testw $_TIF_ALLWORK_MASK, %cx
jne syscall_exit_work
restore_all:
movl PT_EFLAGS(%esp), %eax
movb PT_OLDSS(%esp), %ah
movb PT_CS(%esp), %al
andl $(VM_MASK | (SEGMENT_TI_MASK << 8) | SEGMENT_RPL_MASK), %eax
cmpl $((SEGMENT_LDT << 8) | USER_RPL), %eax
CFI_REMEMBER_STATE
je ldt_ss
restore_nocheck:
TRACE_IRQS_IRET
restore_nocheck_notrace:
RESTORE_REGS
addl $4, %esp
CFI_ADJUST_CFA_OFFSET -4
irq_return:
INTERRUPT_RETURN
.section .fixup,"ax"
iret_exc:
pushl $0
pushl $do_iret_error
jmp error_code
.previous
.section __ex_table,"a"
.align 4
.long irq_return,iret_exc
.previous
CFI_RESTORE_STATE
ldt_ss:
larl PT_OLDSS(%esp), %eax
jnz restore_nocheck
testl $0x00400000, %eax
jnz restore_nocheck
#ifdef CONFIG_PARAVIRT
cmpl $0, pv_info+PARAVIRT_enabled
jne restore_nocheck
#endif
movl PT_OLDESP(%esp), %eax
movl %esp, %edx
call patch_espfix_desc
pushl $__ESPFIX_SS
CFI_ADJUST_CFA_OFFSET 4
pushl %eax
CFI_ADJUST_CFA_OFFSET 4
DISABLE_INTERRUPTS(CLBR_EAX)
TRACE_IRQS_OFF
lss (%esp), %esp
CFI_ADJUST_CFA_OFFSET -8
jmp restore_nocheck
CFI_ENDPROC
ENDPROC(system_call)
其中通过call *sys_call_table(,%eax,4)来调用相应编号的系统调用,参数可以通过ebx、ecx、edx、esi来传送。而sys_call_table的定义如下:
ENTRY(sys_call_table)
.long sys_restart_syscall
.long sys_exit
.long sys_fork
.long sys_read
.long sys_write
.long sys_open
.long sys_close
.long sys_waitpid
.long sys_creat
// ...
系统调用的服务例程都背定义为asmlinkage,表示只从堆栈中获取该函数的参数。