系统调用是作为异常处理的,同其它异常处理handler类似,主要分为三步:
1、保存现场环境(保存各寄存器的值到内核堆栈中)
2、调用相应的系统调用处理函数
3、恢复现场环境,并由内核态切换回用户态
可通过两种方式来调用system call:
1、int 0x80
2、sysenter
这里主要讨论第一种方式
以x86为例:
0x80软中断的handler在trap_init中初始化:start_kernel->trap_init->system_call
963 set_system_trap_gate(SYSCALL_VECTOR, &system_call);
system_call在arch/x86/kernel/entry_32.S中定义:
516 # system call handler stub 517 ENTRY(system_call) 518 RING0_INT_FRAME # can't unwind into user space anyway 519 pushl %eax # save orig_eax 520 CFI_ADJUST_CFA_OFFSET 4 521 SAVE_ALL 522 GET_THREAD_INFO(%ebp) 523 # system call tracing in operation / emulation 524 testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp) 525 jnz syscall_trace_entry 526 cmpl $(nr_syscalls), %eax 527 jae syscall_badsys 528 syscall_call: 529 call *sys_call_table(,%eax,4) 530 movl %eax,PT_EAX(%esp) # store the return value 531 syscall_exit: 532 LOCKDEP_SYS_EXIT 533 DISABLE_INTERRUPTS(CLBR_ANY) # make sure we don't miss an interrupt 534 # setting need_resched or sigpending 535 # between sampling and the iret 536 TRACE_IRQS_OFF 537 movl TI_flags(%ebp), %ecx 538 testl $_TIF_ALLWORK_MASK, %ecx # current->work 539 jne syscall_exit_work 540 541 restore_all: 542 TRACE_IRQS_IRET
1、SAVE_ALL做现场保护
2、call *sys_call_table(,%eax,4)调用相应的系统调用;sys_call_table在arch/x86/kernel/syscall_table_32.S中定义
3、syscall_exit_work恢复现场
主要的处理流程如下图所示:
signal对system call的影响:
有一类系统调用不能立即完成,可能阻塞当前调用进程,将进程状态置为TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE。
当进程处于TASK_INTERRUPTIBLE状态,收到其它进程发给它的signal时,内核会将该进程置为TASK_RUNNING,进程可以被调度并重新运行;这样就导致了系统调用还没有完成,并返回 EINTR, ERESTARTNOHAND, ERESTART_RESTARTBLOCK, ERESTARTSYS, 或 ERESTARTNOINTR等错误码
当上述情况出现时,内核根据错误码做不同的处理:
1、结束系统调用,并从系统调用的下一条指令执行
2、重新执行,返回到用户空间后重新执行系统调用指令
3、看情况,如果信号设置了SA_RESTART标志,则重新执行;否则,返回EINTR错误码,并结束系统调用
系统调用的重启:
do_notify_resume->do_signal(arch/x86/kernel/signal.c)
824 /* Did we come from a system call? */ 825 if (syscall_get_nr(current, regs) >= 0) { 826 /* Restart the system call - no handlers present */ 827 switch (syscall_get_error(current, regs)) { 828 case -ERESTARTNOHAND: 829 case -ERESTARTSYS: 830 case -ERESTARTNOINTR: 831 regs->ax = regs->orig_ax; 832 regs->ip -= 2; 833 break; 834 835 case -ERESTART_RESTARTBLOCK: 836 regs->ax = NR_restart_syscall; 837 regs->ip -= 2; 838 break; 839 } 840 }