Linux 0.11 内核解析:中断相关(1)asm.s文件中断处理分析

0 源代码

有两个版本的,一个是带中文注释,Intel格式的;一个是不带注释是AT&T格式的。

Linux 0.11 中文注释版
Linux 0.11 源码,基于《Linux内核完全注释》赵炯

1 asm.s 文件

我们先假设该文件处理的中断是无特权过渡的情况(具体是不是暂时不知道)。

我们看一下,当中断发生且被检测到之后,硬件做了什么。
在这里插入图片描述
我们可以看到,中断被检测到之后,一些必要的信息被压栈了。
在这里插入图片描述

在80386手册中提到,中断发生之后,一些必要的,为了中断程序结束之后返回原程序继续执行的信息,会被压栈,就像最开始那个图看到的内样,这里分成了4种情况,我们只讨论无特权转换的两种情况,分别输有出错码的异常和无处错码的异常。

这个压栈过程是硬件完成的,回顾一下我们之前的知识
在这里插入图片描述

在中断发生之后,80386执行了

  1. 保存断点(将EFLAGS,CS,EIP依次压栈)
  2. 识别中断源

至于怎么通过中断号,找到对应的中断服务程序,我们后面说。

2 无处错码中断

源代码在kernel/asm.s文件中

_divide_error:  # int0 除法错中断,中断错误码是0pushl $_do_divide_error  # 用于打印除法错中断的错误信息的函数,在traps.c实现# 中断处理程序的地址入栈
no_error_code:# 保护现场xchgl %eax,(%esp) # eax的值入栈,eax保存中断处理程序地址pushl %ebxpushl %ecxpushl %edxpushl %edipushl %esipushl %ebppush %ds 		# 占4字节而非2字节push %espush %fspushl $0		# "error code" 中断有无错误码,是有相关规定的lea 44(%esp),%edxpushl %edx# 设置内核数据段选择符,设置好数据寻址的基址movl $0x10,%edxmov %dx,%dsmov %dx,%esmov %dx,%fscall *%eax	# 执行中断处理程序,C语言函数do_divide_error# 恢复现场addl $8,%esppop %fspop %espop %dspopl %ebppopl %esipopl %edipopl %edxpopl %ecxpopl %ebxpopl %eaxiret # 返回中断处理之前的程序,继续执行后续指令_debug:pushl $_do_int3		# _do_debugjmp no_error_code

我们通过画堆栈内存图的方式分析一下这个过程。

2.1 检测中断后,硬件完成的事情

硬件完成的事情其实就是压栈,压栈之后的内存图是:
在这里插入图片描述

2.2 进入中断服务程序

在识别到中断源之后,通过某种方式,CPU获取的中断服务程序的CS:EIP,进入了终端服务程序,我们这里以debug为例子。

注意,所有的无出错号的中断,默认当成出错号是0,会在保护现场的时候入栈,这个其实是配合实际出错号是0的除法错中断,先不管它。

pushl $_do_int3		# _do_debug 中断服务程序主体,中断处理程序
jmp no_error_code

执行完这两条指令,内存分布是:
在这里插入图片描述
然后就跳转到了no_error_code

它主要分成三大部分

  1. 保护现场
  2. 执行中断处理程序
  3. 恢复现场和返回原程序

下面是保护现场的部分

# 保护现场xchgl %eax,(%esp) # eax的值入栈,eax保存中断处理程序地址pushl %ebxpushl %ecxpushl %edxpushl %edipushl %esipushl %ebppush %ds 		# 占4字节而非2字节push %espush %fspushl $0		# "error code" 中断有无错误码,是有相关规定的lea 44(%esp),%edxpushl %edx

然后是执行中断处理程序的部分,会有数据段选择符是设定,先暂时不管,这个其实就是类似于8086设置ds的值。

	# 设置内核数据段选择符,设置好数据寻址的基址movl $0x10,%edxmov %dx,%dsmov %dx,%esmov %dx,%fscall *%eax	# 执行中断处理程序,C语言函数do_debug

紧接着是恢复现场和返回原程序

# 恢复现场addl $8,%esppop %fspop %espop %dspopl %ebppopl %esipopl %edipopl %edxpopl %ecxpopl %ebxpopl %eaxiret # 返回中断处理之前的程序,继续执行后续指令

这里需要补充

iret的作用

中断门或陷阱门中断服务例程的返回,按照之前入栈的相反顺序将EIP、CS先后出栈;再将EFLAGS标志寄存器出栈,恢复现场(IRET指令)。

简而言之,就是把最开始保存断点的部分依次出栈,从而回到源程序。

我们看一下这个过程的内存分布
在这里插入图片描述

其中

  • esp0对应push $0
  • esp1对应pushl %edx
  • esp2对应addl $8,%esp
  • esp3对应popl %eax

还有一个比较值得关心的

call指令执行前后,寄存器的变化

这一点,使用C语言分析一下函数调用,看看前后变化,在学堂在线《Linux内核分析》中科大孟宁老师MOOC讲解很清晰。


后面的内些的无处错码的函数,与上面的例子完全一样

_debug:pushl $_do_int3		# _do_debugjmp no_error_code_nmi:pushl $_do_nmijmp no_error_code_int3:pushl $_do_int3jmp no_error_code_overflow:pushl $_do_overflowjmp no_error_code_bounds:pushl $_do_boundsjmp no_error_code_invalid_op:pushl $_do_invalid_opjmp no_error_code_coprocessor_segment_overrun:pushl $_do_coprocessor_segment_overrunjmp no_error_code_reserved:pushl $_do_reservedjmp no_error_code_irq13:pushl %eaxxorb %al,%aloutb %al,$0xF0movb $0x20,%aloutb %al,$0x20jmp 1f
1:	jmp 1f
1:	outb %al,$0xA0popl %eaxjmp _coprocessor_error

这些都一样,其中比较特别的是,Linux0.11实现了几个用户自定义中断_irq13就是int45

2.2.1 其他细节的说明

这里保存中断号0,以及保存最开始的堆栈地址esp的值,似乎没啥用,以后再说吧。

这里比较奇怪的是,除法零中断,它的错误码是0,使用的是无处错码的处理程序,如果错误码在之前就被压入栈的话,iret指令执行之前,指向的应该是error code 0,而不是eip,除非这个除法零中断的处理比较特殊,不将出错码压栈,与其他无处错码的中断等同对待,才比较合理。以后再看看这个细节,暂时先放放。

3 有出错码中断

结构与无处错码中断基本一致,这里简要说明一下区别。

首先,硬件处理有区别,压栈之后,会把出错码压栈。

在这里插入图片描述
然后,在保存现场的处理上,有一些不同。

_double_fault:pushl $_do_double_fault
error_code:xchgl %eax,4(%esp)		# error code <-> %eaxxchgl %ebx,(%esp)		# &function <-> %ebxpushl %ecxpushl %edxpushl %edipushl %esipushl %ebppush %dspush %espush %fspushl %eax			# error codelea 44(%esp),%eax		# offsetpushl %eaxmovl $0x10,%eaxmov %ax,%dsmov %ax,%esmov %ax,%fscall *%ebxaddl $8,%esppop %fspop %espop %dspopl %ebppopl %esipopl %edipopl %edxpopl %ecxpopl %ebxpopl %eaxiret

看最开始的指令
在这里插入图片描述
上图是将中断处理函数压栈之后。

然后执行

xchgl %eax,4(%esp)		# error code <-> %eax
xchgl %ebx,(%esp)		# &function <-> %ebx

分别将

  1. eax与栈内的error code交换,eax入栈
  2. ebx保存中断处理函数地址,ebx入栈

然后,在压入错误码的时候,压入的是pushl %eax # error code,这个时候error code就是动态的了,取决于之前硬件压入的错误码是多少,所以,之前的除法零是静态的0,极大概率可能不压入错误码,当成固定机制处理,也可能与兼容性有关。

然后就是调用中断处理函数的时候,执行call *%ebx,因为这次是ebx存放函数地址。

其他的就没有差别了,需要注意的是,error code最开始就被替换下来了,在后面才压栈。

错误码压栈的作用

好吧,看起来错误码压栈之后,好像根本没有使用就略过了啊,肯定不会的,我们看看手册。
在这里插入图片描述

某种类型的异常也导致错误码压栈。异常处理器也能够使用错误码帮助检测异常。

这一点说明了

  1. 错误码的功能:帮助检查异常
  2. 某些有错误码的异常会压栈,不是所有的都会把错误码压栈,比如除法零错误,很可能就不压栈

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/384609.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【精华文】C语言结构体特殊情况分析:结构体指针 / 基本数据类型指针,指向其他结构体

参考链接&#xff1a;Structure pointer pointing to different structure instance 注&#xff1a;可以查看此篇的问题和唯一的回复&#xff0c;那是相对正确的&#xff0c;不要看comment&#xff0c;有很多错误。 我是拒绝分析这种问题的&#xff0c;因为似乎没有人会这么乱用…

enum in c language

今天说说C语言中的枚举。 参考&#xff1a;Enumeration (or enum) in C 1 定义 定义一个枚举类型很容易&#xff1a; enum aa { a1, a2, a3 };这里 enum是关键字aa是枚举变量&#xff0c;也就是我们自定义类型a1,a2,a3是枚举成员 然后怎么使用呢&#xff1f; 首先&#…

信号量SIGCHLD的使用,如何让父进程得知子进程执行结束,如何让父进程区分多个子进程的结束

本教程基于 Ubuntu 20.10 gcc 10.2.0. 示例程序如果不能正常编译和执行&#xff0c;说明您系统和工具版本与我的不匹配&#xff0c;请自行查阅资料。 0 概述 先给出该信号的描述&#xff1a; SignalValueDescriptionSIGCHLD17Child status has changed (POSIX). Signal sent …

UNIX哲学

参考&#xff1a; 对比Linux与Windows 使用Linux想要做某些事情的时候&#xff0c;就拆开想&#xff0c;想想我需要哪些功能&#xff0c;需要哪些工具&#xff0c;依次怎么执行&#xff0c;然后用管道建立连接&#xff0c;让数据依次流过不同的工具&#xff0c;从而得到最终结果…

fork创建多个子进程

references: [1] how to create two processes from a single Parent [2] fork() in C [3] linux中fork同时创建多个子进程的方法 fork的本质&#xff0c;就是复制&#xff0c;把当前进程复制一份&#xff0c;然后两个进程并发地执行fork后面的语句&#xff0c;区别就是&#x…

wait系统调用

reference:Wait System Call in C 只强调几点&#xff0c;剩下的直接看参考链接内容就好了&#xff0c;不是偷懒&#xff0c;而是里面内容写的很好了&#xff0c;没必要再写一遍了&#xff0c;这种东西就是单纯的系统调用而已&#xff0c;理解了功能&#xff0c;就完事了&#…

正则表达式特别需要注意的点:“空“字符的匹配

在正则表达式中&#xff0c;[...]代表1个字符&#xff0c;不管里面有多少字符&#xff0c;最终这个东西的结果都是1个字符。 对于表达式[^a]表达的匹配除了a之外的字符&#xff0c;并且是1个字符。 需要注意的是&#xff0c;有些特殊字符是不会被匹配的。 我们看一个示例&am…

vim多列操作--插入/删除

插入 How to insert text at beginning of a multi-line selection in vi/VimVim Commands 删除 ctrl v使用上下左右键选中一片区域按d删除

vim进行行内某部分的复制剪切粘贴

ctrl v使用方向键选中你要复制的部分 按d&#xff08;剪切&#xff09;或者按y&#xff08;复制&#xff09;再移动到你的目标位置&#xff0c;按p粘贴&#xff08;在正常模式下才行&#xff0c;如果不是&#xff0c;先按esc&#xff09; 这个过程与你操作word文档的复制粘贴…

函数调用堆栈

基于孟宁老师的Linux内核分析 1 int g(int x){ 2 int y x 3;3 return y;4 }5 6 int f(int x){7 int z x 10;8 return g(z);9 }10 11 int main(){12 int a f(8) 1;13 return 0;14…

Vivado提高综合和实现的速度

让计算机的资源尽可能给vivado&#xff0c;综合、实现的时候修改一个参数 jobs改为你的计算机的最大值&#xff0c;我的计算机是12核的。 速度会快很多&#xff01;

安装Ubuntu RISC V toolchain失败(网速、git配置原因)

git获取大容量工程出错&#xff1a;RPC failed&#xff1b; curl GnuTLS recv error : Decryption has failed. error: RPC failed; curl 56 GnuTLS recv error (-54): Error in the pull function.fatal: The remote end 官方GitHub仓库 gitee镜像仓库 如果网速不够&#xff0…

VirtualBox Ubuntu个人配置

注意这里VT-x启用&#xff0c;除了在BIOS启用CPU虚拟化&#xff0c;还得在命令行设置一次才可以勾选。 F:\>cd F:\VirtualBox # 进入VirtualBox安装目录F:\VirtualBox>VBoxManage.exe list vms # 查找所有虚拟机 "rhel64" {240f96d8-6535-431d-892e-b70f3dc4…

Ubuntu停止维护版本的软件源配置和系统升级方法

这里以Ubuntu 20.10版本为例&#xff08;当前是2022.2.14&#xff0c;该版本已经停止维护&#xff09;&#xff0c;我们现在需要正常使用该版本&#xff0c;并且期待升级到21.10版本&#xff0c;我们需要 配置正确是软件源升级该版本 配置正确的软件源 配置国内镜像源 我们…

diff and colordiff on Ubuntu

在Ubuntu中使用diff来对比文件差异&#xff0c;但是不是很好用&#xff0c;尤其是着色方面&#xff0c;用起来很麻烦&#xff0c;因此可以安装colordiff。 我们有两个文件file1和file2&#xff0c;使用命令 colordiff file1 file2 -y -B -W 140就可以对比文件差异&#xff0c…

帮助你成为高手的视频和资料

1. 为什么大多数人不会真正成功 博客链接 视频链接 2. TED演讲&#xff1a;真正拉开你与周围人之家差距的&#xff0c;是自学能力 视频链接 3 埃隆马斯克&#xff1a;第一性原理&#xff0c;少用类比&#xff0c;类比多了就不能抓住本质了 4 如何成为一个顶尖高手 文章链…

【数据结构】快速排序非递归算法及其改进

在学数据结构中排序这一章节的时候&#xff0c;有一道有关快速排序的作业题描述如下&#xff1a; 按下述要求编写快速排序的非递归算法&#xff1a; 定义一个栈&#xff08;或队列&#xff09;&#xff0c;把整个序列的上、下界入栈&#xff08;或队列&#xff09;。当栈&#…

【数据结构】对快速排序原理的理解(图解,通俗易懂)

学习数据结构时&#xff0c;书本上直接给出了快速排序的过程以及代码&#xff0c;对其原理解释的不够详细&#xff0c;琢磨代码后&#xff0c;发现其原理其实十分简单&#xff0c;简述如下&#xff1a; &#xff08;1&#xff09;在待排序列中找一个“中枢元素”&#xff08;书…

【离散数学】图论基础知识

文章目录1 图的基本概念2 图的连通性3 图的矩阵表示4 几种特殊的图4.1 二部图4.2 欧拉图4.3 哈密顿图4.4 平面图5 无向树6 生成树1 图的基本概念 无向图&#xff1a; 简而言之&#xff0c;边不带方向的图就是无向图。 有向图&#xff1a; 简而言之&#xff0c;边带方向的图就…

【运筹与优化】单纯形法解线性规划问题(matlab实现)

文章目录单纯形法步骤&#xff1a;1.将线性规划问题化为标准形式2.列出单纯形表3.进行最优性检验4.从一个基可行解转换到另一个目标值更大的基可行解&#xff0c;列出新的单纯形表5.重复3、4直到计算结束为止举例单纯形法matlab实现单纯形法是一种解线性规划问题的算法&#xf…