分析system_call中断处理过程
来源:互联网 发布:快手上的淘宝优惠群 编辑:程序博客网 时间:2024/06/10 02:30
章强 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
实验截图
github上下载menu最新代码,上传到实验楼git,讲menu复制到LinuxKernel目录下,修改test.c文件
在menu目录下执行 make rootfs
改造后的MenuOS系统会自动编译并启动运行
用gdb调试:
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S
打开另一个shell窗口,,执行:
gdb //打开gdb调试file linux-3.18.6/vmlinux //加载符号表target remote:1234 //连接端口b sys_getpid //设置断点
c 继续执行list 查看断点处代码n 进行单步调试
如果继续进行单步调试的话,会进入汇编语言部分,此时就无法看到代码执行的细节了
分析
系统调用在内核代码中的工作机制和初始化
一、从int 0x80触发一个系统调用到执行系统调用处理的函数sys_time,到后边返回用户态整体过程理解
1,什么时候把0x80和system_call绑定起来的?什么时候初始化中断向量?
2,system_call处理过程以及哪里调用了sys_xyz(xyz和sys_xyz是通过系统调用号匹配起来的),xyz和system_call是通过中断向量匹配起来的。
3 ret_from_sys_call的单步调试过程
初始化:
可以看到代码有:set_system_trap_gate包含系统调用中断向量和system_call代码入口。
之后系统一旦出现int 0x80就会立即调用system_call。
二、系统调用也是一种中断,所以也存在着保存和恢复现场的问题,下图可看到SAVE_ALL;
call sys_call_table %eax是系统调用表,在menuos中即为sys_time;
之后syscall_after_call:先保存返回值,退出之前有jne syscall_exit_work如果没有这个就直接restore_all返回用户态
进入复杂的syscall_exit_work,里边会有进程调度时机。
简化成伪代码:
exit时会检查当前任务current->work需不需要syscall_exit_work去处理,不需要直接ireturn
当前进程有一些信号处理或需要调度就需要syscall_exit_work去处理了:
流程是跳转到work_pending,里边有jz work_notifysing处理信号,
和work_resched需要重新调度,就call schedule,调用完成后再跳转到restore_all把他返回系统调用;
三、浏览system_call到iret之间的代码
system_call:
syscall_exit_work:
work_pending:
调用schedule,调用完之后可以跳转restore_all,把后面恢复现场工作结束掉。
总结
系统调用”是操作系统提供给用户程序进行调用的一些服务。这些服务是系统预先提供的函数,在这一点上系统调用与普通的用户程序是没有区别的。而区别则在于“系统调用”是由操作系统提供给用户的,这些服务更接近底层或者要求的安全性更高,因此由操作系统来统一实现和管理。
处理器在eax寄存器中拿到系统调用号之后,会到系统调用表中找到该系统调用所对应的入口函数地址,然后执行该函数。
函数的入口地址在在syscall.c(entry_32s)中。
中断处理中读取中断号及参数,然后找到中断服务例程并执行,退出中断后进行堆栈切换,返回用户态,继续执行用户程序。
系统调用system_call的处理过程
syscall_call 函数到系统调用服务例程通过系统调用号联系起来:在上面执行软中断 0x80 时,系统调用号会被放入eax寄存器(参数的传递),system_call 函数读取eax寄存器获取参数(当前系统调用的调用号),将其乘以4生成偏移地址。然后以中断向量表(sys_call_table)为基址,以系统调用号所确定的为偏移地址相加得到最后的物理地址:基址+偏移地址 => 系统调用服务例程的地址。其中 sys_call_table 基址在文件 arch/x86/kernel/syscall_table_32.S 中定义,同时表中每一项例程的地址占用4个字节,所以上面乘以4。
参数保存方式:由于系统调用例程在定义时时用 asmlinkage 标记,所以编译器仅从堆栈中获取该函数的参数。在进入system_call函数前,用户应用会把参数存放到寄存器中,system_call 函数执行时会首先把这些寄存器压入堆栈。这样对系统调用服务例程可以直接从堆栈照片能够获取参数。
从system_call开始到iret结束之间的过程
从整体过程来看,系统通过 int 0x80 从用户态进入内核态。在这个过程中系统先保存了中断环境,然后执行系统调用函数。system_call() 函数通过系统调用号查找系统调用表 sys_cal_table 来查找到具体的系统调用服务进程。在执行完系统调用后在执行 iret 之前,内核做了一系列检查,用于检查是否有新的中断产生。如果没有新的中断,则通过已保存的系统中断环境返回用户态。这样就完成了一个系统调用过程。
系统调用通过 INT 0x80 进入内核,跳转到 system_call() 函数,然后执行相应服务进程。因为代表了用户进程,所以这个过程并不属于中断上下文,而是属于进程上下文!这点一定要分清楚。
无论是中断返回(ret_from_intr) ,还是系统调用返回,都使用了 work_pending 和resume_userspace。对于宏SAVE_ALL来说,这条语句会把将寄存器的值压入堆栈当中,压入堆栈的顺序对应struct pt_regs出栈时这些值传递到struct pt_regs的成员,实现从汇编代码向C程序传递参数。struct pt_regs可以在arch/x86/include/asm/ptrace.h中查看。用户态到内核态需要int 0x80进行中断,只有生成了中断向量后才可以切换状态。中断处理让CPU停止当前工作转为执行系统内核中预设的一些任务,因此必须要对当前CPU执行的任务进行执行现场的保护工作,并对一些其他工作进行检查,完成调用后,再进行检查,才能执行iret返回。系统内部调用涉及CPU架构等内容,不同的CPU对于系统调用的汇编具体代码是不一样的。
- 分析system_call中断处理过程
- system_call中断处理过程分析
- system_call中断处理过程分析
- 分析system_call中断处理过程
- 分析system_call中断处理过程
- 分析system_call中断处理过程
- 分析system_call中断处理过程
- 分析system_call中断处理过程
- 分析system_call中断处理过程
- 分析system_call中断处理过程
- system_call中断处理过程分析
- 分析system_call中断处理过程
- 分析system_call中断处理过程
- 分析system_call中断处理过程
- 分析system_call中断处理过程
- 分析system_call中断处理过程
- 分析system_call中断处理过程
- 分析system_call中断处理过程
- poj3268
- C++——数的进制和数据间隔
- 使用css3实现翻书效果(一)
- View拖动实现
- C# 基于iTextSharp封装的PDF操作类
- 分析system_call中断处理过程
- CVPR2016代码合集
- pwnable.kr writeup hash
- 链表相关面试题(一)
- dfs-104. Maximum Depth of Binary Tree
- android UI 五大布局
- SSH实现批量删除
- 产品经理的要学习的知识
- Effective C++ 读书笔记