朱宇轲 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
本次作业将继续上次的实验,用gdb追踪系统mkdir执行系统调用的过程,并分析system_call的中断处理过程。
首先应当将上次写的代码载入menu中,重新装载menu:
之后打开test.c文件,在其中加入上次写的调用mkdir函数的定义。
执行make rootfs,从而打开menu镜像,我们发现mkdir就装载进menu的命令中了:
然后我们用gdb在sys_mkdir处设置断点。
如果继续进行单步调试的话,可能会进入汇编语言部分,此时就无法看到代码执行的细节了。所以说,用常规的gdb方法是不能对具体的内核代码进行调试的。如果想去对这部分进行调试,有兴趣的可以进一步查阅资料去了解一下。
system_call的代码还是比较复杂的,所幸老师在课上为我们对它的代码进行了简化,本博客将围绕这个代码来进行分析。
.macro INTERRUPT_RETURN iret .endm .macro SAVE_ALL ... .endm .macro RESTORE_INT_REGS ... .endm ENTRY(system_call) SAVE_ALL syscall_call: call *sys_call_table(,%eax,4) movl %eax,PT_EAX(%esp) syscall_exit: testl $TIF_ALLOWORK_MASK,%ecx 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: ... END(work_pending)
代码从ENTRY(system_call)处正式进入程序。此时实际上已经触发了中断,首先通过SAVE_ALL将进程和中断上下文存储起来;之后,通过调用sys_call_table(,%eax,4)执行具体的系统调用(在这里是sys_mkdir),并在系统调用执行结束后将返回值存储到esp中。
之后,需要检测是否需要进入syscall_exit_work。如果不需要的话,就通过RESTORE_INIT_REGS将之前存储的上下文重新载入,并调用iret结束中断,返回之前的进程。
syscall_exit_work执行了一些进程调度、消息传递的工作,如果进行了进程切换,可能要继续触发新的中断,执行新的系统调用。
system_call的流程图简图如图所示(边上的圆框表示具体的执行代码):
总结:
本文对system_call的执行流程进行了初步的分析。通过阅读代码得知:在中断处理的过程中,需要保存之前程序的上下文,并执行相关的系统调用。此时若执行进程调度和切换,则可能触发新的中断,并保存当前的环境,如此层层堆叠,直到回到最初调用的程序中。