- 问题描述
通过这一周的学习,我进一步了深入内核系统的调用处理过程,更加完整地理解了系统调用的工作机制。下面通过给MenuOs增加geteuid命令,以及通过gdb跟踪调用time函数的过程,并分析system_call代码对应的工作过程这两方面深入理解系统调用的工作机制。
2. 解决步骤
2.1 给MenuOs增加geteuid命令
首先进入LinuxKernel目录,删除旧的menu目录,然后在github上克隆一个新的目录menu,进入menu,命令如下
cd LinuxKernel
rm -rf menu
git clone https://github.com/menging/menu.git
cd menu
修改test.c文件,增加Geteuid函数(函数名避免与系统调用geteuid重复)
使用如下命令生成根文件系统,并查看帮助,并运行geteuid命令
make rootfs
MenuOs>> help
MenuOs>> geteuid
2.2 gdb跟踪调用调试time函数
2.2.1 调试步骤
返回LinuxKernel目录,shift+ctrl+o水平分割,执行如下命令
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -S -s
在gdb调试之前,先启动gdb,把3.18.6的内核加载进来,之后连接到target remote 1234,操作完成后,就连接到了需要调试的MenuOS。
在start_kernel处设置断点,命令如下
gdb
file linux-3.18.6/vmlinux
target remote:1234
b start_kernel
c
在start_kernel处停下,time调用是13号系统调用对应的内核处理函数,即sys_time。接下来就可以在这里用b sys_time设置一个断点,启动MenuOS执行time命令,程序就会停到sys_time这个函数的位置,time命令执行到一半将卡在那里。
在gdb中使用list命令,查看断点附件代码
在gdb中使用命令s跟踪调试
2.2.2 system_call汇编代码分析
以上步骤主要跟踪了一个系统调用time,其中涉及系统调用对应的中断处理过程system_call,接下来将仔细分析system_call这个中断服务程序。
system_call代码比较复杂,这里将代码简化并加上注释
ENTRY(system_call)
RING0_INT_FRAME
ASM_CLAC
pushl_cfi %eax #保存系统调用号
SAVE_ALL #保存现场,将用到的所有CPU寄存器保存在栈中
GET_THREAD_INFO(%ebp) #ebp用于存放当前进程thread_info的结构地址
testl $_TIE_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
jnz syscall_trace_entry
cmpl $(nr_syscalls),%eax #检查系统调用号
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_IEQS_IRET #恢复现场
irq_return:
INTERRUPT_RETURE #iret
system_call流程示意图
3. 总结
通过这一周学习,我对linux内核系统调用处理过程有了更深入的了解,并且初步完整地学习了系统调用的工作机制。