系统调用实验(下):
将第四章的两个实验集成到MenuOS系统中,将其作为MenuOS系统的两个命令,新版本的menu中已经把两个系统调用添加进去了,只需重新克隆一个新版本的menu。
使用make rootf 打开menu镜像,可以看到MenuOS菜单中新增了两条命令,time和time-asm。
使用gdb跟踪系统调用内核函数sys_time
同第二个实验,打开gdb,在函数中设置断点,按c继续执行,在断点处停下来,可以用list查看这段代码。
(实验楼的环境非常卡,经常崩坏)
使用s进行单步执行,会进入get_seconds()函数,使用finish将函数执行完,之后再单步执行,一直到return i代码获取时间数值。
继续单步执行,将显示Cannot find bounds of current function,这是因为sys_time返回后进入汇编代码处理gdb无法继续跟踪。
执行int 0x80之后执行system_call对应的代码,在system_call处设置断点后执行,发现函数无法在处停止,说明system_call是一个特殊的函数,实际上其不是一个函数,只是一段汇编代码的起点,gdb不能跟踪它。
系统调用的工作,一旦在start_kernel初始化好了以后,运行到init x80指令之后,立刻跳转到systime_call这个位置,entry(system_call),中断系统调用处理过程,存在保护现场,恢复现场的功能, sys_call_table(%eax,4) 系统调用表,eax传过来的是系统调用号
syscall_after_call: 保存返回值
syscall_exit: 处理退出工作 有可能发生进程调度,有可能处理信号
restore all 返回用户态
system_call的代码:
ENTRY(system_call)
RING0_INT_FRAME
ASM_CLAC
pushl_cfi %eax //保存系统调用号;
SAVE_ALL //可以用到的所有CPU寄存器保存到栈中
GET_THREAD_INFO(%ebp) //ebp用于存放当前进程thread_info结构的地址
testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
jnz syscall_trace_entry
cmpl $(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
使用流程图来理解system_call流程
小结:
系统调用是特殊的中断函数,是多种中断处理过程的集合。通过INT指令发起调用,通过IRET指令用于返回系统调用到用户态结束。
init ox80是通过中断向量将信息传递给system_call,system_call是通过系统调用号于sys_syz()进行通信