系统调用的三层机制(下)
(一)给MenuOS增加命令
(1)打开虚拟机,首先用rm -rf menu指令删除当前的menu目录,然后用git clone重新克隆一个新版本的menu,进入menu,运行make rootfs脚本结果报错
(2)经过与同学的讨论,我发现问题出在应该提前进入LinuxKernel目录,重新打开一个新的shell,按照新的流程走一遍,运行MenuOS系统,输入help命令后发现支持的命令增加了两个:time(显示系统时间)和time-asm(用汇编方式显示系统时间)
(二)使用gdb跟踪系统调用内核函数sys_time
(1)用如下方式调试内核,首先用命令启动内核:
cd ..#返回到LinuxKernel目录下
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -S -s
(2)在进行gdb调试前先启动gdb,把3.18.6的内核加载进来,之后连接到target remote:1234,操作完成后就连接到需要调试的MenuOS了。接下来设置断点:
-
在start_kernel处设置断点,按c停下;
-
time系统调用对应的是内核处理函数——sys_time,用b sys_time设置断点,启动Menu并执行time命令,程序停在函数这个位置,通过list命令列出sys_time对应的代码如下图所示
(3)用gdb的finish命令把函数全部执行完,再单步执行一直到return i。
(三)system_call汇编代码中的系统调用内核处理函数
一旦执行int 0x80,CPU就直接跳转到system_call这个位置来执行,即系统调用的工作机制在start_kernel这里初始化之后,CPU一遇到int 0x80就会立即跳转到sys_call的位置,system_call中断服务程序的入口这段会变代码的处理过程是非常重要的,它是系统调用的处理过程,而系统调用是一个特殊的中断,在中断过程中有保护现场和恢复现场,这段代码里面同样也有保护现场SAVE_ALL和恢复现场restore_all的过程。
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*/