Linux内核分析第五周作业

分析system_call中断处理过程

这次的目标是通过gdb来跟踪上周选择的uname系统调用。因为系统调用是通过中断在内核态实现的,gdb无法调试本机的系统调用。所以必须像之前的内核跟踪那样,用gdb远程连接至qemu虚拟机进行跟踪。

1. 首先修改之前的MenuOS,添加一个myuname函数通过API的方式调用uname系统调用,直接复制了上周的代码,注意要在main函数中把这个函数添加到菜单中

用make命令编译以后,会生成一个test的可执行文件,可以先在本地运行试一试

可以看到命令运行成功。

接下来就制作rootfs并且把MenuOS放在里面。然后启动qemu并指定initrd为刚才制作的rootfs。

可以看到uname也成功地运行在qemu中了。

2. 下面使用gdb进行远程调试。

首先qemu的启动参数要加上-s来打开远程调试端口。

然后gdb加载Linux内核的符号表,并且连接到qemu的调试端口1234

gdb
(gdb)file linux-3.18.6/vmlinux
(gdb)target remote:1234

在系统调用的入口——system_call函数加断点,然后让qemu继续运行

接下来在qemu中执行系统调用,输入uname命令,居然没有断下来 w(?Д?)w 果然和教程中说的一样。

因为system_call是汇编写的代码,直接用gdb直接查找“system_call”符号得到的地址不准确,但是也不会差的太远。尝试从刚才的断点0xc174d881前面15个字节开始反汇编20条指令,看看情况

果然0xc174d881并不是一个有效的代码地址。于是把断点直接放在0xc174d884上,就是system_call的真正起始地址

这次就成功地断了下来

然后对照着entry_32.S单步跟踪,看看究竟做了什么

这一大堆的push显然就是SAVE_ALL所做的事情,将当前的寄存器都保存起来。

这里把EAX和0x166比较了一下。EAX里面是系统调用号,0x166是十进制的358。看一眼系统调用表,最后一个系统调用号正好是357。因此这里是说如果传入的EAX超过了最大的系统调用号,就进入异常处理程序syscall_badsys

而如果是有效的系统调用号,就call一个地址。因此再看一眼-0x3e8aede0地址附近的内容,根据无符号整数的定义,-0x3e8aede0其实就是0xc1751220

这里面似乎都是地址。随便挑第二个地址查看一下

正好就是sys_exit的入口地址,而sys_exit就是1号系统调用exit的实现函数。

参考一下源文件

-0x3e8aede0就应该是sys_call_table的地址了。至于为什么用奇怪的负数地址,可能与CPU指令有关,也许这样的指令比较短  ╮(╯_╰)╭

因此这里call的是EAX对应的系统调用的入口地址。

uname的实现函数sys_newuname上周已经看过了

很简单,uname的内容一直在内核中保存着,每次系统调用只要将内容从内核态复制到用户态即可,并且复制的时候用锁保护了一下。

从sys_newuname返回以后,继续执行system_call

源文件是这样的

EAX是系统调用实现函数的返回值(uname用它返回错误码),这里把EAX保存在ESP+0x18的位置。再回顾一下system_call的开头,SAVE_ALL是如何保存现场的

之后在system_call中ESP就没有变过,因此ESP+0x18正好是EAX保存的地方。这样就把之前保存的EAX用函数的返回值替换掉了,而等到最后RESTORE_ALL的时候就会自动地把返回值恢复到EAX中

接下来就该进入收尾工作了

syscall_exit_work里面有一个进程调度的机会

------------------------------------

可惜实验中似乎条件不满足,没有发生调度。而是直接恢复了全部的寄存器

最后清空了堆栈,并且用iret指令从中断返回

整个系统调用的简单流程图如下

3. 总结

系统调用的方法是通过0x80中断进入内核态,然后内核根据EAX中的系统调用号去调用对应的系统调用函数,最后将返回值通过EAX返回给用户态程序。

此外,系统调用是进程调度切换的时机,因为此时控制交给了内核。对于用户程序来说仅仅是调用了系统调用,然后从系统调用返回。在系统调用中发生的进程切换对于用户程序是透明的。

对于一般的中断处理函数,首先要保存现场,最后要恢复现场

4. API与中断调用的区别

其实在实验中,系统调用的方式是通过汇编调用0x80中断,类似于上周的uname_asm程序。因为在设置了正确的system_call入口地址以后,用系统调用的API依然无法触发断点。经过跟踪代码,终于发现了原因。

教程中也提到过,从Pentium II开始新加入了一个专用的指令,快速系统调用——SYSENTER。而API都非常优化,自然使用的是新指令。因此中断0x80的IRQ system_call自然就不会运行,所以断点没有生效。

实际上sysenter的入口也在entry_32.S里面,叫做ia32_sysenter_target,而且代码、流程都和system_call很类似,感兴趣的话可以阅读一下。

王岩

原创作品转载请注明出处

《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

时间: 03-17

Linux内核分析第五周作业的相关文章

魏昊卿——《Linux内核分析》第二周作业:了解操作系统是怎样工作的

魏昊卿——<Linux内核分析>第二周作业:了解操作系统是怎样工作的 一.实验部分 使用实验楼的虚拟机打开shell cd LinuxKernel/linux-3.9.4 qemu -kernel arch/x86/boot/bzImage 然后cd mykernel 您可以看到qemu窗口输出的内容的代码mymain.c和myinterrupt.c 使用自己的Linux系统环境搭建过程参见mykernel,其中也可以找到一个简单的时间片轮转多道程序内核代码 mymain.c myinterr

Linux内核分析第六周作业

分析Linux内核创建一个新进程的过程 首先更新MenuOS的代码,加入调用fork的命令.吐槽一句,实验楼免费用户无法连网.还好只要去github复制一段代码即可 先观察一下fork命令的实现 1 int Fork(int argc, char *argv[]) 2 { 3 int pid; 4 /* fork another process */ 5 pid = fork(); 6 if (pid<0) 7 { 8 /* error occurred */ 9 fprintf(stderr,

Linux内核分析第七周作业

Linux内核如何装载和启动一个可执行程序 有了上次的教训,这次直接用vmware完成 (- ̄3 ̄)- 先观察MenuOS新增的函数 1 int Exec(int argc, char *argv[]) 2 { 3 int pid; 4 /* fork another process */ 5 pid = fork(); 6 if (pid < 0) 7 { 8 /* error occurred */ 9 fprintf(stderr,"Fork Failed!"); 10 e

linux内核分析第五周-分析system_call中断处理过程

本实验目的:通过以一个简单的menu小程序,跟踪系统调用的过程,分析与总结系统调用的机制和三层进入的过程. 实验原理:系统调用处理过程与中断处理的机制 系统调用是通过软中断指令 INT 0x80 实现的,而这条INT 0x80指令就被封装在C库的函数中.(软中断和我们常说的硬中断不同之处在于,软中断是由指令触发的,而不是由硬件外设引起的.)INT 0x80 这条指令的执行会让系统跳转到一个预设的内核空间地址,它指向系统调用处理程序,即system_call函数. system_call函数是怎么

Linux内核分析——第五周学习笔记

扒开系统调用的三层皮[下] 前言:以下笔记除了一些讲解视频中的概念记录,图示.图示中的补充文字.总结.分析.小结部分均是个人理解.如有错误观点,请多指教! 视频中所要求的画出流程图在图二中有体现. PS.实验操作会在提交到MOOC网站的博客中写.

linux内核分析 第五周 扒开系统调用的三层皮(下)

rm menu -rf 强制删除原menu文件 git clone http://github.com/mengning/menu.git 从github中克隆 cd menu 在test.c中增加上周编写的两个函数:Getpid()和GetpidAsm(),修改test.c中的main函数,添加两行代码MenuConfig make rootfs这一步之后会打开menu,输入help后可以看到当前的系统调用: 分别执行刚加进去的两个系统调用: 插入断点进行调试: 从system_call开始到

LINUX内核分析第七周学习总结——可执行程序的装载

LINUX内核分析第六周学习总结——进程的描述和进程的创建 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.知识概要 (一)预处理.编译.链接和目标文件的格式 1.可执行程序是怎么得来的 2.目标文件的格式ELF 3.静态链接的ELF可执行文件和进程的地址空间 (二)可执行程序.共享库和动态加载 1.装载可执行程序之前的工作 2.装载时动态链接和运行时动态链接应用举例 (三)

LINUX内核分析第六周学习总结——进程的描述和进程的创建

LINUX内核分析第六周学习总结——进程的描述和进程的创建 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.知识概要 进程的描述 进程描述符task_struct数据结构(一) 进程描述符task_struct数据结构(二) 进程的创建 进程的创建概览及fork一个进程的用户态代码 理解进程创建过程复杂代码的方法 浏览进程创建过程相关的关键代码 创建的新进程是从哪里开始执行的

LINUX内核分析第八周学习总结——进程的切换和系统的一般执行过程

LINUX内核分析第八周学习总结——进程的切换和系统的一般执行过程 黄韧(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.知识概要 Linux中进程调度的基本概念与相关知识 schedule函数如何实现进程调度 Linux进程的执行过程(一般情况与特殊情况) 宏观描述Linux系统执行 二.学习笔记 (一)进程切换的关键代码switch_to分析 进程进度与进程调度的时机分析 1.