分析system_call中断处理过程
“20135224陈实 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 “
第一部分:
利用GDB打开系统调用函数并实现跟踪调试
具体步骤:
1 选取上周函数并利用原来的getpid的简单函数来实现本次实验
2 使用gdb跟踪分析使用在sys_time处设置断点并list找到的代码
3 (注意)进入system_call的时候gdb无法继续跟踪,要提前结束直接quit退出即可
主函数内容:
第二部分:
大致过程:SYSterm_call---运行到SAVE—ALL(保护现场)继续运行----table表找对应程序----iret结束返回
分析call对应函数功能(简化)
GET_THREAD_INFO(%ebp):
将当前信息保存在ebp
testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%esp) jnz syscall_trace_entry:
判断是否 trace调用
cmpl $(NR_syscalls), %eax jae syscall_badsys:
判断系统调用号是否超出最大值
call *sys_call_table(,%eax,4):
系统调用的数字实际上是一个序列号,表示其在系统的一个数组sys_call_table[]中的位置。
movl %eax,PT_EAX(%esp):
保存系统调用的返回值
DISABLE_INTERRUPTS(CLBR_ANY):
屏蔽其他系统调用
movl TI_flags(%esp), %eax:
寄存器ecx是通用寄存器,在保护模式中,可以作为内存偏移指针(此时,DS作为 寄存器或段选择器),此时为返回到系统调用之前做准备
testl $_TIF_ALLWORK_MASK, %eax jne syscall_exit_work :
退出系统调用之前,检查是否需要处理信号
RESTORE_REGS 4
:x86架构恢复寄存器代码
INTERRUPT_RETURN
即iret: 系统调用是通过软中断指令
INT 0x80 实现的,而这条INT 0x80指令就被封装在C库的函数中。(软中断和我们常说的硬中断不同之处在于,软中断是由指令触发的,而不是由硬件外设引起的。)
INT 0x80 这条指令的执行会让系统跳转到一个预设的内核空间地址,它指向系统调用处理程序,即system_call函数。
总结
system_call函数是怎么找到具体的系统调用服务例程路径:
通过系统调用号查找系统调用表sys_call_table,软中断指令INT 0x80执行时,系统调用号会被放入 eax 寄存器中,system_call函数可以读取eax寄存器获取,然后将其乘以4,生成偏移地址,然后以sys_call_table为基址,基址加上偏移地址,就可以得到具体的系统调用服务例程的地址了!然后就到了系统调用服务例程了。需要说明的是,系统调用服务例程只会从堆栈里获取参数,所以在system_call执行前,会先将参数存放在寄存器中,system_call执行时会首先将这些寄存器压入堆栈。system_call退出后,用户可以从寄存器中获得(被修改过的)参数。
注意事项:系统调用通过软中断INT 0x80陷入内核,跳转到系统调用处理程序system_call函数,然后执行相应的服务例程。但是由于是代表用户进程,所以这个执行过程并不属于中断上下文,而是进程上下文。因此,系统调用执行过程中,可以访问用户进程的许多信息,可以被其他进程抢占,可以休眠。当系统调用完成后,把控制权交回到发起调用的用户进程前,内核会有一次调度。如果发现有优先级更高的进程或当前进程的时间片用完,那么会选择优先级更高的进程或重新选择进程执行。