余星光 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
一、实验内容
1、使用gdb跟踪分析一个系统调用内核函数,推荐在实验楼环境下完成实验;
2、根据本周所学知识分析系统调用的过程,从system_call开始到iret结束之间的整个过程,并画出简要准确的流程图。
二、实验过程
1、进入实验楼,打开命令窗口,输入如下命令
cd Linuxkernel rm menu -rf git clone http://github.com/mengning/menu.git cd menu然后打开test.c文件,在其中加入相应的代码,如下:
这样就将自己的系统调用命令加入到系统中了
然后输入如下命令:
make rootfs启动系统,可以看到如下效果
输入help命令,我们可以看到自己定义的两个系统调用命令已添加到系统中,即多了getpid和getpid-asm两个命令。
用gdb跟踪分析一个系统调用内核函数这里不再赘述,可以参考前面写的文章跟踪分析Linux内核的启动过程。
2、分析system_call开始到iret结束之间的整个过程
system_call汇编伪代码如下:
.macro INTERRUPT_RETURN ; 中断返回 iret .endm .macro SAVE_ALL ; 保护现场 ... .macro RESTORE_INT_REGS ... .endm ENTRY(system_call) SAVE_ALL syscall_call: call *sys_call_table(,%eax,4) movl %eax, PT_EAX(%esp) ; store the return value syscall exit: testl $_TIF_ALLWORK_MASK, %ecx # current->work jne syscall_exit_work restore_all: RESTORE_INT_REGS irq_return: INTERRUPT_RETURN ; 到这里就算执行完了 ENDPROC(system_call) syscall_exit_work: testl $_TIF_WORK_SYSCALL_EXIT, %ecx jz work_pending END(syscall_exit_work) work_pending: testb $_TIF_NEED_RESCHED, %cl jz work_notifysig work_resched: call schedule jz restore_all work_notifysig: ... ; deal with pending signals END(work_pending)对于宏SAVE_ALL来说,这条语句会保存当前线程的现场,然后是执行system_call,跳转到相应系统调用号所对应的服务例程当中,也就是在sys_call_table表中找到了相应的函数入口点,然后进入到系统调用表查找到系统调用服务程序的入口函数的地址,再进行跳转,这样便完成了对sys_call_table的进入。接下来伪代码马上就来到了syscall_exit,在执行完syscall_call后,马上接触并退出系统调用,但是这个退出的过程非常的复杂,从syscall_exit开始到irq_return的真实代码:
syscall_exit: LOCKDEP_SYS_EXIT DISABLE_INTERRUPTS(CLBR_ANY) 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 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 irq_return: INTERRUPT_RETURN完成这些其余的工作之后,最终来到了irq_return,在这里使用宏 INTERRUPT_RETURN 实际上就是iret指令,恢复现场,最终完成了系统调用中断处理的返回。
从system_call到iret之间的整个过程可以用如下流程图表示:
三、总结
系统调用可以抽象成是很多种不同的中断处理过程的集合。