中断、异常和系统调用(2-1,2-2,2-3)

2023-12-13 05:20:09

2-1 课堂练习2.1:外部中断4b3f3b31d8614fc3849ff3dd41e5813f.png

本实训分析 Linux 0.11 对外部中断的响应和处理过程。在每条指令执行的末尾,如果没有关中断,CPU 会检查是否收到了外部中断信号,如果有信号,则 CPU 就切换到核心态去执行对应的中断处理程序,在处理完毕后,会执行 iret 这个中断返回指令,回到原状态(一般是用户态),继续执行原程序。

第1关时钟中断的发生

任务描述

?本关任务:通过实际操作回答在输出第一行 0/1 字符的过程中(如下图所示),共发生了几次时钟中断?

?

2383d54084864e372bf938f81ca54c85.jpeg

相关知识

为了完成本关任务,你需要掌握: 1.设置版本 1 内核为分析对象; 2.开始用 gdb 调试内核; 3.跟踪分析时钟中断。

设置版本1内核为分析对象

首先解压版本1内核源码。使用cp命令将/data/workspace/myshixun/exp1中的1.tgz复制到~/os/目录下;

?

9fe7ad0a2edecf0fbb1a0d5f27efb388.png

切换到~/os/linux-0.11-lab目录下,将1.tgz解压到当前目录下;

?

162b87456ab2df5f2a586a7d52d499c9.png

然后调整cur的指向。先使用rm -rf curcur删除,再使用ln命令创建符号链接。

?

8aa50e6fdd8c2d49ab93e82282af390e.png

现在可以编译和测试版本 1 内核。首先进入1/linux目录下编译内核;

?

2c6d4e281a9f629849d3239bd93705bf.png

确认内核映像文件Image已经生成;

?

12921279a4a8c061577145f79bf30b6f.png

然后回到目录~/os/linux-0.11-lab,并使用./run启动虚拟机检测内核是否正常;

?

8e0cf05e5338f928d059cf95162996c9.png

如果正常虚拟机在加载完毕之后将会出现如下画面。

?

5dfc9ef6e109a1856de06c4aad455de4.png

第2关第一次时钟中断

?任务描述

分析版本1内核,找到第一次时钟中断的恢复点地址。

相关知识

为了完成本关任务,你需要掌握: 1.用 gdb 调试内核; 2.跟踪分析时钟中断; 3.中断/异常的恢复点分析。

准备阶段

本关卡的分析对象是版本1内核,可以基于前一关卡环境进行后续实验,如果重置实验环境则需要重新将版本1内核设置为分析对象,详见第一关的相关知识。

使用 gdb 调试内核

启动两个终端,在一个终端里切换到目录~/os/linux-0.11-lab,然后运行脚本 rungdb,以启动 bochs 虚拟机并等待 gdb 连接;

?

fb39adf482f75e4c0e25b13125bde245.png

在另一个终端里切换到目录~/os/linux-0.11-lab,然后执行脚本./mygdb,以启动 gdb 并读入符号信息,跟踪到 main 函数入口。

?

6cde3f89f71faf352cfc65de1e57b3d1.png

跟踪分析时钟中断

在函数 do_timer(由时钟中断的处理函数 timer_interrupt 调用)处设置断点; 跟踪到该断点第 1 次出现;

?

856d6739413ad69c964c239bb433766e.png

中断/异常的恢复点分析

当一个中断/异常被 gdb 捕获时,通常正在运行中断处理程序,这时可以继续跟踪,直至回到恢复点指令。以时钟中断为例,为了从函数 do_timer 跟踪到恢复点,可以如下操作:

?

339b2eab61f80852880555a4b67f77b1.png

由上图可见时钟中断处理程序的入口是 timer_interrupt 函数。 跟踪到当前函数(do_timer)执行完毕返回到 timer_interrupt 函数;

?

f4d93e7184dcfdb43aa1881504915c0a.png

跟踪到 timer_interrupt 函数(用汇编语言写的)末尾的 iret 指令;

?

e974bace981824ad514f0712fc2c32c4.png

?

86f5c2e44d628dedf9a1918fd0bee065.png

?

d89d34f7dcb920f7f85c77cb24cefe5b.png

?

a566c623bc409298058bda0ae9dee314.png

?

0add571d6695659e297f179a53cd8396.png

通过单步执行命令 si 来执行该 iret 指令,返回到恢复点;

再通过反汇编命令:disas ,分析恢复点指令的地址。

?

bd33aa890881a8969116a8248ef77bec.png

关闭 gdb 调试: 在评测通关之后为了保证环境的正常,不影响下一关的操作,需要先输入 kill 指令关闭虚拟机,然后输入 quit 退出 gdb 调试。

?

92d8cacc09a7d51c4c8c218d2abd6c2f.png

答案

f48ec4561e8f4de99fb94fca08235aa1.png

第3关第六次时钟中断

?任务描述

本关任务:通过相关知识以及实验回答:版本 1 内核的第 6 次时钟中断发生时,断点和恢复点(指令地址)分别是多少?此时 bochs 虚拟机输出的 0/1 字符串是什么?(忽略空格)

相关知识

为了完成本关任务,你需要掌握: 1.使用 gdb 调试内核; 2.跟踪分析时钟中断。

准备阶段

本关卡基于前面关卡环境进行后续实验,如果重置实验环境请从第一关重新开始。

使用 gdb 调试内核

启动两个终端,在一个终端里切换到目录~/os/linux-0.11-lab,然后运行脚本 rungdb,以启动 bochs 虚拟机并等待 gdb 连接;

fb39adf482f75e4c0e25b13125bde245.png

在另一个终端里切换到目录~/os/linux-0.11-lab,然后执行脚本./mygdb,以启动 gdb 并读入符号信息,跟踪到 main 函数入口。

6cde3f89f71faf352cfc65de1e57b3d1.png

跟踪分析时钟中断

开始用 gdb 调试内核,跟踪到 main 函数入口。方法与第 1 关的步骤(2)一样。 (点击右下角上一关可以直接查看上一关内容,不会对关卡造成影响,但是不要点击评测,会改变环境)

?

da04f0efcf934cade58a30ba4290b125.png

捕获第 6 次时钟中断的发生

方法与第 2 关的步骤(3)类似,跟踪到断点 do_timer 第 6 次出现时即可,此时 jiffies 的值也是 6 。

079358b6bc735f06f5f14ab0abde6859.png

中断/异常的恢复点分析

当一个中断/异常被 gdb 捕获时,通常正在运行中断处理程序,这时可以继续跟踪,直至回到恢复点指令。以时钟中断为例,为了从函数 do_timer 跟踪到恢复点,可以类似如下操作:

339b2eab61f80852880555a4b67f77b1.png

由上图可见时钟中断处理程序的入口是 timer_interrupt 函数。 跟踪到当前函数(do_timer)执行完毕返回到 timer_interrupt 函数;

f4d93e7184dcfdb43aa1881504915c0a.png

跟踪到 timer_interrupt 函数(用汇编语言写的)末尾的 iret 指令;

e974bace981824ad514f0712fc2c32c4.png

86f5c2e44d628dedf9a1918fd0bee065.png

d89d34f7dcb920f7f85c77cb24cefe5b.png

a566c623bc409298058bda0ae9dee314.png

0add571d6695659e297f179a53cd8396.png

使用调试命令 si 来执行该 iret 指令,返回到恢复点;

然后通过反汇编命令:disas ,来分析恢复点指令的地址。

bd33aa890881a8969116a8248ef77bec.png

断点指令和恢复点指令的分析

对于外部中断而言,恢复点指令是断点指令的后一条。需要说明的是,loop 指令的功能是先将 ecx 寄存器减一,然后检查其值,如果其值非 0 ,则继续循环,否则中止循环,执行下一条指令。以如下指令为例:

261cb07d7563b3915b2bba1882063466.png

其功能是:在地址 0x7977 处循环,每次 ecx 寄存器都减一,直到其值为 0 。因此,loop 指令的上一条有可能是它自己。 在 gdb 中查看寄存器值的命令是 info reg:

2fb860eca5566ff37e179fad6bd7ffa9.png

断点指令和恢复点指令的分析

对于外部中断而言,恢复点指令是断点指令的后一条。需要说明的是,loop 指令的功能是先将 ecx 寄存器减一,然后检查其值,如果其值非 0 ,则继续循环,否则中止循环,执行下一条指令。以如下指令为例:

261cb07d7563b3915b2bba1882063466.png

其功能是:在地址 0x7977 处循环,每次 ecx 寄存器都减一,直到其值为 0 。因此,loop 指令的上一条有可能是它自己。 在 gdb 中查看寄存器值的命令是 info reg:

2fb860eca5566ff37e179fad6bd7ffa9.png

答案

?926e836d9ecd4614ab6604004e906b0d.png

2-2 课后作业2.1:外部中断

f6b7fc0ba82e41fcbc3da7be0e1822c1.png

第1关修改版本 1 内核源码,使得每次时钟中断发生时,都在屏幕上输出字符 ‘t’

任务描述

本关任务:修改版本 0 内核,使得每发生 100 次时钟中断,就在屏幕上输出一个字符‘t’和当时的进程号,如“t(0)”表示0号进程运行时发生了时钟中断。

相关知识

为了完成本关任务,你需要掌握: 1.内核态下的字符输出; 2.判断已经发生了多少次时钟中断。

内核态下的字符输出

在版本 0 内核里,可以使用函数printk来输出字符,其用法类似于printf注: 在版本 0 内核中,没有实现用int 0x81指令输出字符这个功能。

printk用法示例:

  1. printk("trying to free inode with count=%d\n",inode->i_count);

判断已经发生了多少次时钟中断

已经发生的时钟中断的次数记录在全局变量 jiffies 中,每发生一次时钟中断,该变量的值就增加 1 。

编程要求

根据相关知识,以及上课内容,对版本 0 内核进行修改,使得每发生 100 次时钟中断,就在屏幕上输出一个字符‘t’和当时的进程号。

?

798133b01b4c9418f74f34951dbf511a.jpeg

使用VScode打开1/linux文件夹,获取版本1内核源代码?

在timer_interrupt函数开头添加输出字符t的汇编代码:

movb $116, %al
int $0x81

回到命令行中重新编译1/linux,再启动./rungdb和./mygdb虚拟机即可过关

?

第2关修改版本 0 内核

任务描述

本关任务:修改版本 0 内核,使得每发生 100 次时钟中断,就在屏幕上输出一个字符‘t’和当时的进程号,如“t(0)”表示0号进程运行时发生了时钟中断。

相关知识

为了完成本关任务,你需要掌握: 1.内核态下的字符输出; 2.判断已经发生了多少次时钟中断。

内核态下的字符输出

在版本 0 内核里,可以使用函数printk来输出字符,其用法类似于printf注: 在版本 0 内核中,没有实现用int 0x81指令输出字符这个功能。

printk用法示例:

  1. printk("trying to free inode with count=%d\n",inode->i_count);

判断已经发生了多少次时钟中断

已经发生的时钟中断的次数记录在全局变量 jiffies 中,每发生一次时钟中断,该变量的值就增加 1 。

编程要求

根据相关知识,以及上课内容,对版本 0 内核进行修改,使得每发生 100 次时钟中断,就在屏幕上输出一个字符‘t’和当时的进程号。

798133b01b4c9418f74f34951dbf511a.jpeg

通过vscode找到do_timer

?在函数中添加

if(jiffies%100==0)
prink("t(%d),sys_getpid());

0c3bf2102ef241d6a19d30a123fc44b1.png

2-3 课堂练习2.2:中断/异常的处理过程

2adb3fd8b71b48dc8ad0871ee3443d76.png
第1关除零异常分析

任务描述

分析版本 1.1 内核,回答下列问题: 1.在函数 main 的语句jiffies = jiffies/0;所对应的汇编指令片段中,有一个 idiv 指令,此指令的地址是多少? 2.在该 idiv 指令执行之前,当前指令位置(CS:EIP)和栈位置(SS:ESP)分别是多少? 3.使用 si 命令执行了该指令后,新指令位置和栈位置分别是多少?此时栈中保存的恢复点位置和用户栈位置分别是多少?

相关知识

为了完成本关任务,你需要掌握: 1.如何设置某版本的内核为分析对象; 2.如何开始用 gdb 调试内核; 3.查看 C 语句编译之后对应的汇编指令片段; 4.分析响应中断/异常时,CPU 做了哪些工作; 5.查看当前寄存器的状态; 6.查看当前栈顶的状态。

实验准备

本关卡使用版本 1.1 内核作为分析对象,内核文件存放在/data/workspace/myshixun/exp1文件夹中,可以将其解压到linux-0.11-lab下使用。

如何设置某版本的内核为分析对象

下面以版本1内核为例进行讲解。

首先解压版本1内核源码。使用cp命令将/data/workspace/myshixun/exp1中的1.1.tgz复制到~/os/目录下;

?

999b1e2dfa4a8f36d0483dbaf916d3e7.png

切换到~/os/linux-0.11-lab目录下,将1.1.tgz解压到当前目录下;

?

3a603d5b30a6bea396c4d212aa11a356.png

然后调整cur的指向。先使用rm -rf curcur删除,再使用ln命令创建符号链接。

?

f817a078e0dee351de7f5ae5ca1a43d5.png

如何开始用 gdb 调试内核

先关闭bochs虚拟机,然后打开两个终端,其中一个终端在linux-0.11-lab目录下运行rungdb脚本,以启动 bochs 虚拟机并等待 gdb 连接;

?

0a9fbd9c0eda12ba44ef06620c633f66.png

在另一个终端里切换到目录~/os/linux-0.11-lab/,然后启动脚本./mygdb,这个命令会启动 gdb 并读入内核符号信息,同时会通过执行0.gdb中的调试命令来连接到 bochs 虚拟机,并进而跟踪到 main 函数入口。

?

ad2758c0c025462af4b5dd27b2033c90.png

查看 C 语句编译之后对应的汇编指令片段

如果要查看某条 C 语句编译之后对应的汇编指令片段,可以在该 C 语句处设置断点,并跟踪到该断点,然后反汇编,所看到的当前指令之后的一段汇编指令就对应于该 C 语句。

例如,jiffies = jiffies/0;是文件 main.c 的第 147 行,可以如下方式查看:

?

f71f154488a060d2d5442394bf52ab89.jpeg

上面显示的汇编指令中,有一行前面有箭头标识,此即为当前指令,即马上将要执行的指令。

分析响应中断/异常时,CPU 做了哪些工作

  • 切换到核心栈,并在其中保存中断现场。

  • 转到中断处理程序去运行,并切换到核心态。

如下图所示:

?

191170f9899ade434ead937079e1e661.jpeg

上图显示了栈中中断现场的结构,(OLD SS:OLD ESP) 描述了用户栈顶的位置,(OLD CS:OLD EIP) 描述了恢复点的位置。

如何查看当前寄存器的状态

使用 gdb 调试命令 info registers 即可,如下所示:

?

25535aa441f717b46be4c56645e902f4.png

也可以单独查看某一个寄存器:

?

39d81b9955ec1e327d2ddc1f4815a13b.png

如何查看当前栈顶的状态

可以使用命令 x 来查看:

?

7f39a5279296643d46a894ddfbe1e420.png

上面显示了栈顶的 5 个长字,是某异常发生时的中断现场,其中存储的用户栈顶的位置是 0x17:0x2573c ,存储的恢复点的位置是 0xf:0x7967 。需要注意的是,x86 中栈是从高地址向低地址方向增长的,这里的栈顶位置是 0x1fa0c 。

编程要求

根据相关知识,回答问题:(将答案填写在/data/workspace/myshixun/第一关.txt中) 1.在函数 main 的语句jiffies = jiffies/0;所对应的汇编指令片段中,有一个 idiv 指令,此指令的地址是多少? 2.在该 idiv 指令执行之前,当前指令位置(CS:EIP)和栈位置(SS:ESP)分别是多少? 3.使用 si 命令执行了该指令后,新指令位置和栈位置分别是多少?此时栈中保存的恢复点位置和用户栈位置分别是多少?

答案

8fbe5069e94c43b884c44855d08a511b.png

?

文章来源:https://blog.csdn.net/han_lx_/article/details/134860952
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。