《Linux内核分析》
第五章 系统调用的三层机制(下)
5.1 给MenuOS增加命令
- 强制删除当前menu目录,用get clone重新克隆一个新版本的menu,运行make rootfs脚本可以自动编译并生成根目录文件系统,还可以运行MenuOS系统:
cd LinuxKernel
rm -rf menu
git clone https://github.com/mengning/menu.git
cd menu
make rootfs
- 输入help命令可以发现,当前支持的命令比之前多了。增加了两个命令:time(功能是显示系统时间),还有一个是time_asm(功能是使用汇编的方式来显示时间):
5.2 使用gdb跟踪系统调用内核函数sys_time
- 启动内核。
cd ..
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -S -s
- 水平分割,启动gdb,把3.18.6的内核加载进来,连接到target remote 123。
- 在start_kernel处设置断点。
- 用 b sys_time 设置一个断点,启动MenuOS后执行time命令,程序停在sys_time这个函数的位置。
gdb调试也可以看到Breakpoint在linux-3.18.6/kernel/time/time.c中的这个文件。因为是用宏来实现的,所以无法直接看到sys_time。
用list命令列出sys_time对应的代码如下:
用finish命令把这个函数全部执行完后,再单步执行,一直到return i,获得的就是当前系统时间time的数值。
再设置一个断点system_call,执行time,发现time函数返回了。
但是在执行time-asm时,还是停在了原先设定的sys_time这个位置,在system_call这个位置不能停下来
system_call代码片段,是一段特殊的汇编代码,只能调试系统调用的内核函数和其他内核函数的处理过程,但gdb不能跟踪entry_32.s这个汇编代码。
实验楼每次执行到这步的时候都卡死!
5.3 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