分析 system_call 中断处理过程
一、实验过程和分析
1、给MenuOS增加chmod命令(上一周选择的系统调用),并使用 gdb 跟踪分析
打开menu文件下的test.c的文件,添加chmod函数代码并在主函数中添加以下代码:
MenuConfig("chmod","Modifying File Permissions",Chmod);
如下图所示:
重新编译后运行MenuOS,结果如下图所示:
对chmod命令所用到的系统调用内核函数进行调试跟踪,命令如下:
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -S -s
同之前调试方法一样,启动gdb,加载内核,连接到target remote:1234
,在start_kernel
和sys_chmod
分别设置断点后继续运行,并在MenuOS启动后执行chmod命令,如下图所示:
可以发现程序停在sys_chmod(定义在fs/open.c中)处,用宏来实现。然后继续单步执行,会出现“Cannot find bounds of current function”,直到返回系统调用位置。按照视频教学,尝试在system_call处设置一个断点,但是还是停在了sys_chmod处,system_call 不是正常的函数,是一段特殊的汇编代码,gdb还不能进行跟踪。
2、分析从 system_call 开始到 iret 结束之间的整个过程
通过课本知识讲解明白了在 start_kernel 中的 trap_init()(定义在/arch/x86/kernel/traps.c中)里将中断向量0x80和system_call中断服务程序入口进行了绑定:
#ifdef CONFIG_X86_32
set_system_trap_gate(SYSCALL_VECTOR, &system_call); //绑定中断向量0x80和system_call中断服务程序入口,其中SYSCALL_VECTOR定义为0x80
set_bit(SYSCALL_VECTOR, used_vectors);
#endif
system_call中断服务程序入口代码如下:
ENTRY(system_call)
RING0_INT_FRAME #can't unwind into user space anyway
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) #合法,对照系统调用号在系统调用表中寻找相应服务例程
syscall_after_call:
movl %eax,PT_EAX(%esp) #保存返回值到栈中
syscall_exit:
LOCKDEP_SYS_EXIT
DISABLE_INTERRUPTS(CLBR_ANY) # make sure we don't miss an interrupt
# setting need_resched or sigpending
# between sampling and the iret
TRACE_IRQS_OFF
movl TI_flags(%ebp), %ecx
testl $_TIF_ALLWORK_MASK, %ecx #检查是否有任务需要处理
jne syscall_exit_work #需要,进入 syscall_exit_work(最常见的进程调度时机)
restore_all:
TRACE_IRQS_IRET #恢复现场
restore_all_notrace:
#ifdef CONFIG_X86_ESPFIX32
movl PT_EFLAGS(%esp), %eax # mix EFLAGS, SS and CS
# Warning: PT_OLDSS(%esp) contains the wrong/random values if we
# are returning to the kernel.
# See comments in process.c:copy_thread() for details.
movb PT_OLDSS(%esp), %ah
movb PT_CS(%esp), %al
andl $(X86_EFLAGS_VM | (SEGMENT_TI_MASK << 8) | SEGMENT_RPL_MASK), %eax
cmpl $((SEGMENT_LDT << 8) | USER_RPL), %eax
CFI_REMEMBER_STATE
je ldt_ss # returning to user-space with LDT SS
#endif
restore_nocheck:
RESTORE_REGS 4 # skip orig_eax/error_code
irq_return:
INTERRUPT_RETURN #iret
其大概流程图和分析如下所示:
二、问题
在本地虚拟机上进行内核跟踪调试加载不出调试信息(no debugging symbols found),如下图所示:
解决方案:
linux-3.18.6中通过运行make menuconfig
来进行个别的调整,这里需要开启"Compile the kernel with debug info"选项:
进入linux-3.18.6/目录下,运行make menuconfig
,选择Kernel hacking —>Compile-time checks and compiler options —> [ ] Compile the kernel with debug info
示意图如下,利用键盘选中debug选项,然后敲"Y"勾选:
之后再次进行编译make
,便可以解决问题。