分析system_call中断处理过程
1.给Menu OS 增加命令
首先,重新克隆一个新版本的menu,实验过程如下:
结果如图:
其次,我们要通过gedit test.c在test.c中新增一个helloworld命令:
保存之后,重新编译执行,结果如图:
2.使用gdb跟踪分析一个系统调用内核函数
首先,使用如下命令启动内核:
此时,内核处于stopped状态。
另外打开一个shell端口,使用gdb设置断点跟踪调试系统调用内核函数sys_time:
单步执行直到return i;
3.实验分析及总结
首先,系统通过中断向量0x80来进入system_call中断服务程序入口,进入中断服务程序从而触发系统调用,其中,system_call的代码简化如下:
ENTRY(system_call)
RINGO_INT_FRAME
ASM_CLAC
push1_cfi %eax /*保存系统调用号*/
SAVE_ALL /*保存现场,将用到的所有CPU寄存器保存到栈中*/
GET_THREAD_INFO(%ebp) /*ebp用于存放当前进程thread_info结构的地址*/
test1 $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
jnz syscall_trace_entry
cmp1 $(nr_syscalls),%eax /*检查系统调用号(系统调用号应小于NR_syscalls)*/
jae syscall_badsys /*不合法,跳入异常处理*/
syscall_call:
call *sys_call_table(,%eax,4) /*合法,对照系统调用号在系统调用表中寻找相应的系统调用的内核处理函数*/
movl %eax,PT_EAX(%esp) /*保存返回值到栈中*/
syscall_exit:
testl $_TIF_ALLWORK_MASK, %ecx /*检查是否需要处理信号*/
jne syscall_exit_work /*需要,进入 syscall_exit_work*/
restore_all:
TRACE_IRQS_IRET /*不需要,执行restore_all恢复,返回用户态*/
irq_return:
INTERRUPT_RETURN /*相当于iret*/
在这段代码中,首先是保存现场,然后系统根据保存在寄存器eax中的系统调用号来查找sys_call_table系统调用表中的相应位置,调用对应的系统调用处理函数;如果匹配到并合法,系统则会去执行该系统调用处理函数(在上述实验中sys_time系统调用处理函数即是在call *sys_call_table(,%eax,4)这里被调用的),并把返回值保存在寄存器eax中,从系统调用处理函数返回之后再把返回值压入栈中保存;之后,它还会在syscall_exit里面判断当前的任务是否需要处理syscall_exit_work,如果需要,则进入syscall_exit_work,检查此时是否有等待调度的进程,如果有,则进行调度,其中还有可能涉及到work_notifying处理信号以及work_resched重新调度。调度完之后系统会跳转到restore_all恢复现场,并iret返回系统调用到用户态。以下为system_call的流程示意图: