计算机是如何运行的:从CPU到内存管理

零、以一个程序的运行作为引子

一个程序是如何运行的?这看起来是个简单的问题,实际上涉及到了许多复杂的内容:

  1. 计算机体系架构(CPU-内存-I/O设备-外存);
  2. 数据在计算机硬盘与内存中的保存形式;
  3. CPU如何进行运算(加法器);
  4. CPU中的寄存器如何存储需要运算的数据(通用寄存器);
  5. CPU中的寄存器如何知道要怎么处理数据(指令寄存器–控制总线);
  6. CPU如何从内存中传输数据与控制(数据总线与控制总线);
  7. CPU如何对内存地址进行定位(分段、分页);
  8. IA-16与IA-32的发展史(向上兼容、新的功能)
  9. 内存地址是如何分配使用的(线性地址与物理地址,寻址方法);
  10. 分段与分页式管理方法的意义;
  11. 进程的概念是什么;
  12. 进程如何管理一个程序;
  13. 进程与CPU和内存的关系是什么(通过总线相连,进行数据交换);
  14. (更为复杂的)多进程之间的资源争抢问题(锁、死锁);
  15. 中断(I/O中断与内存缺页中断等)
  16. 操作系统设计的权限划分(用户空间与内核空间);
  17. 如何用栈传递参数?函数的开始和结束的本质是什么(内存空间、CPU栈等角度)?

参考书籍:

  1. 《x86汇编语言-从实模式到保护模式》作为入门
  2. 《汇编语言》 王爽, 作为x86-16的详细学习
  3. 《计算机组成原理》

一、CPU的工作原理

CPU的发展历史

此处我们主要考虑的是8086这一经典的CPU型号,以及80386包含了分页功能的CPU型号。
为了从原理角度理解CPU的运行,首先以8086为例进行说明,而先不考虑更加高级的分页内存管理方式。(此处可以参考王爽《汇编语言》)

计算机是如何运行的:从CPU到内存管理_第1张图片

CPU的组成

在逻辑角度上来看,CPU要想进行计算,需要具备什么样的功能呢?
i) 首先需要有计算能力(当然该能力首先就默认CPU具有数据存储的能力)。
ii) 第二应该具有数据传输能力,用于和内存之间进行数据的传递;
iii) 还应该有控制线,即控制下一步做些什么操作;

基于以上三点需求,满足后就可以从最简单的角度上允许CPU进行计算。
那么CPU是如何逻辑划分的呢?从硬件角度上又包含什么呢?

首先引入一个概念:寄存器
寄存器是CPU中的存储单元。其逻辑结构都相同,如8086CPU中所有寄存器都是16位的,
计算机是如何运行的:从CPU到内存管理_第2张图片
显然,该寄存器一次能存放一个16Bit的数据,即2^16=65536。

按照上面的需求,寄存器被分为如下几种类型:

  1. 内存数据寄存器(MDR):是一个内存缓存的寄存器,用于存放一个数据值本身。这个数据值可能会存放到MAR中,也可能是从MAR中获得的。
  2. 内存地址寄存器(MAR):用于存放某个内存单元的地址,将内存单元地址存入该寄存器,便可取出,从而获得内存中该处的值。
  3. 指令寄存器(IR):存放当前正在处理的指令。
  4. 程序计数器(PC或IP):存放当前要执行的地址。

指令寄存器能够执行的功能:
mov
add
jmp

那么有了以上几种类型的寄存器,CPU是如何执行一段内存中的指令的呢?
首先需要知道CPU访问内存地址的方式,才能将指令和数据从内存读取到CPU中。

如上面所说,CPU与内存之间有三大总线:地址总线(用于传输地址)、数据总线(用于传输数据)、控制总线(用于传输指令)。而对于CPU来说,只有知道了内存的物理地址才能进行实际的操作。

CPU的内存寻址方式

对于8086CPU来说,其采用的是分段式处理方式。CPU中的相关部件提供了两个16位的地址,一个是段地址,一个时段内偏移地址。将段地址和偏移进行移位相加(*16),可以获得实际的物理地址。

其实这是一种通用的寻址方法。即:

物理地址 = 基础地址 + 偏移地址

问题:段地址如何给出?

分段的意义?

指令的定义

无论数据还是指令,都是一些数字。而区分开来便有了意义。
对于指令,从人为定义的角度上规定好了某些数字的含义,这便是指令集。

CPU中寄存器的数目与种类?
从硬件角度上来看,CPU是什么样子的?

问:为什么设计者将寄存器进行分类,而不和内存一样采取通用的存储方式?
答:可能是角色不同:CPU需要运算,而运算的时候,其步骤可以被整理为通用的步骤,因此将其进行预先定义,有利于实现。而内存只是为了存储数据,因此无需(也不能)进行预先定义某个内存地址的作用。

CPU与内存的通信

是通过总线来实现的。分为三种类型的总线:

  1. 数据总线:一次能够传送数据的线路。通常与CPU的字长一致。我们说的32位、64位就是指数据总线宽度。
  2. 地址总线:其位数决定了CPU能够直接进行内存寻址的大小。但对于16位机,使用了段式结构来用段和段内偏移实现实现了“用16位总线传递20位数据”的效果。到32位则还有不同的处理方法,后文会详述。
  3. 控制总线:用来传送控制信号和时序信号。

二、内存的组成

内存的硬件结构

内存的物理地址编码

内存的寻址

内存管理:分段

什么是内存分段

为什么发明了分段式内存管理

在x86-16体系中,为了解决16位寄存器对20位地址线的寻址问题,引入了分段式内存管理。
而CPU则使用CS,DS,ES,SS等寄存器来保存程序的段首地址。当CPU执行指令需要访问内存时,只会送出段内的偏移地址,而通过指令的类型类确定访问那一个段寄存器。
在x86-32体系中,为了向上兼容,保留了分段内存管理。

分段内存管理是如何实现的

如何使用分段内存管理

CPU如何进行内存寻址

CS:IP结构:

内存管理:分页

什么是内存分页

页框与寻址

分页管理的好处是什么

分页管理与分段管理的区别是什么?

段页式内存管理

分段内存管理的优势在于内存共享和安全控制,而分页内存管理的优势在于提高内利用率。他们之间并不是相互对立的竞争关系,而是可以相互补充的。也就是可以把2种方式结合起来,也就是目前计算机中最普遍采用的段页式内存管理。段页式管理的核心就是对内存进行分段,对每个段进行分页。这样在拥有了分段的优势的同时,可以更加合理的使用内存的物理页。
计算机是如何运行的:从CPU到内存管理_第3张图片

段页式地址转换

计算机是如何运行的:从CPU到内存管理_第4张图片
地址转换的过程。实际上就是我们前面介绍的分段和分页地址转换的结合。

  1. CPU给出要访问的逻辑地址;
  2. 通过分段内存管理的地址转换机制,将逻辑地址转换为线性地址,也就是分页系统中的虚拟地址;
  3. 通过分页内存管理的地址转换机制,将虚拟地址转换为物理地址;

CPU如何对一个分页管理的内存系统进行寻址?

中断: 缺页中断

什么是缺页中断
缺页中断是如何运作的?

虚拟内存

为什么要发展处虚拟内存的概念?

随着计算机CPU的发展, 以及更多的进程需要更多, 更复杂的内存需求, 当一个程序没有空间可用就不好了, 另外内存还很容易被破坏, 从而造成很多不可预知的, 难以定位的问题. 因此操作系统发展出了一种对主存的抽象概念, 叫做虚拟内存.
虚拟内存是硬件异常, 硬件地址翻译, 主存, 磁盘文件, 和内核软件的完美交互. 它为每个进程提供了一个大的, 一致的, 和私有的地址空间.
这样, 多个进程间相互不受影响(当然也引入了进程间通信的一个课题)从而不会破坏其他进程的内存.
虚拟内存还将主存看成一个存储在磁盘上的地址空间的高速缓存, 在主存中只保存活动区域, 并根据需要在磁盘和主存之间来回传送数据, 通过这种方式, 可以高效地使用主存.

虚拟内存与物理内存之间的关系如何维护

内存寻址的发展

物理寻址(Physical Addressing)

内存由M个连续的单个字节大小的单元组成的数组, 每个字节都有一个唯一的物理地址(Physical Address), 为0,1,2,…,M-1依次排布.
可以理解, 从本质上来讲, CPU只有获得内存的物理地址,才能通过寄存器与CPU进行数据交换. (下图参考[1] p560)
早期PC都是使用物理寻址.
计算机是如何运行的:从CPU到内存管理_第5张图片

虚拟寻址(Virtual Addressing)

使用虚拟寻址的话, CPU通过生成一个虚拟地址(Virtual Address)来访问主存, 这个虚拟地址在被送到内存之前先转换为对应的物理地址, 这个过程被称为地址翻译(Address Translation). 地址翻译这一过程通过一个被称为内存管理单元(Memory Management Unit)的专用硬件完成, 利用存放在主存中的查询表(即页表)来动态翻译虚拟地址. 该表的内容由操作系统管理… (下图参考[1] p560)
计算机是如何运行的:从CPU到内存管理_第6张图片

对于虚拟内存来说, 虚拟内存被分割为大小固定的页(Page), 物理内存也被分割为大小固定的块(被称为页框(PageFrame)).从而两者之间可以有三种关系:

  1. 未分配的: VM系统还未分配(或创建)的页.未分配的块没有任何数据与它们相关联, 因此不占用任何磁盘空间.
  2. 缓存的: 当前已缓存在物理内存中的已分配页;
  3. 未缓存的: 未缓存在物理内存中的已分配页.

此处理解得再深入一点, 所谓的"虚拟内存" 实际上就是指磁盘, 因为它被虚拟化为内存.

页表

参考 [1],p563
PTE: Page Table Entry.
计算机是如何运行的:从CPU到内存管理_第7张图片

读页表时机:

每次地址翻译硬件将一个虚拟地址转换为一个物理地址时,都会读取页表.
计算机是如何运行的:从CPU到内存管理_第8张图片

缺页中断:

此处所讲的"命中" 实际上就是缺页的本质: 当命中的时候, 便意味着已经将磁盘中的某个页框读到了内存中, 即物理内存中缓存了这个磁盘页框, 从而使缓存命中.
[1] p564
计算机是如何运行的:从CPU到内存管理_第9张图片

修改页表时机:

此处以DRAM缓存命中和缓存不命中(缺页)两个可能性来分析MMU的工作过程.

计算机是如何运行的:从CPU到内存管理_第10张图片
如上图所示, 假设CPU的读请求可以缓存命中, 则其流程是这样的:

  1. CPU生成一个虚拟地址, 并将其传送给MMU;
  2. MMU生成PTE地址, 并从高速缓存/主存中请求得到它;
  3. 高速缓存/主存向MMU返回PTE;
  4. MMU构造物理地址, 并将其传给高速缓存/主存;
  5. 高速缓存/主存返回所请求的数据字给CPU.

计算机是如何运行的:从CPU到内存管理_第11张图片
页面命中完全由硬件处理. 与之不同的是, 处理缺页要求硬件和操作系统内核协作完成.如b)所示:

  1. 第一步到第三步: 与a)中的第1步到第3步相同;

4… PTE中的有效位是0, 所以MMU触发一次异常, 传递CPU中的控制到操作系统内核中的缺页异常处理程序;
5… 缺页处理程序确定出物理内存中的牺牲页, 如果这个页面已经被修改了, 则把它换出到磁盘.
6… 缺页处理程序页面调入新的页面, 并更新内存中的PTE;
7… 缺页处理程序返回到原来的进程, 再次执行导致缺页的命令. CPU将之前引起缺页的虚拟地址重新发送给MMU. 因为虚拟页面现在缓存在物理内存中, 所以就会命中. 在MMU执行了图9-13b中的步骤后, 主存就会将所请求的字返回给CPU.

修改页表的方法:

多级页表

为什么需要多级页表:
之前的一级页表设计中, 有一个缺陷: 对于给定的内存规格, 一开始就需要全部申请好资源用于管理. 比如一个32位的地址空间, 4KB的页面和一个4字节的PTE, 那么即使我们只使用了其地址空间中的一小部分, 我们也需要整个4MB大小的页表占用内存资源.(计算方式: 2 ^ 32 B / 4KB * 4B ).
这还是只针对32位来说的. 针对64位的则更加复杂. 因此可以想到的方法就是建立多级页表, 从粗粒度到细粒度来多级协同管理.
此处的方法是建立一个二级页表. 第一级页表中的每个PTE负责映射虚拟地址空间中的一个4MB的片(chunk), 这里每一片都是有1024个连续的页面组成的.假设地址空间是4GB, 那么1024个PTE已经足够覆盖整个空间了.
而细粒度的二级页表中的每个PTE则负责映射一个4KB的虚拟内存页面.
这种方法减少了内存浪费: 加入一级页表中的某个PTE是空的, 则其二级页表就不存在, 也就不会占用内存资源.

mmap


四、CPU与内存的关系

计算机是如何运行的:从CPU到内存管理_第12张图片

寄存器

什么是寄存器,寄存器的作用是什么

寄存器是CPU的重要组成部分,用于在CPU中存储数据。
按照CPU位数的不同,一个寄存器能够存放的数据量也不同。以8086为例,寄存器为16位,因此一次只能存放16bit的数据。(因此对于20bit的物理内存地址寻址需求的话,就需要进行合并操作)。
按照需求,寄存器有很多中类型。

8086CPU为例

CPU是怎样从内存获得数据、存储数据、计算数据、输出数据到内存呢?搞明白了这一点,就明白了需要什么样的寄存器。
众所周知,内存是一种线性结构。先不考虑高级的分页、虚拟内存等设计,简单地按照分段内存来考虑。一个内存地址能够表示一个字节的数据,CPU需要知道物理内存地址才能通过总线(具体什么总线?)根据物理地址将该物理地址中的数据传入到CPU中。
那么内存中是如何存储数据的呢?
代码和数据等不同类型的数据是否有区分?答案是否定的,内存被设计为一种通用的存储介质,只负责在给定的物理地址存取数据,而不会区分其是代码还是数据,还是其他的一些东西。
这些东西只有在载入CPU相应寄存器的时候才有了意义。也就是内存中相同的物理地址的数据,被载入到数据的寄存器中,他就是一个数字,被载入到指令寄存器中,他就是一个指令。
此时需要引出寄存器的种类和具体操作了。
计算机是如何运行的:从CPU到内存管理_第13张图片

先说寻址

CPU要与内存进行数据交互,首先需要解决的问题就是内存的寻址问题。众所周知CPU需要内存的物理地址,才能找到需要读写内存的哪个区域的数据。此时需要一个寄存器来保存物理内存地址。8086通过段地址和偏移地址来描述物理地址。可以看到通过段地址和偏移地址的组合,可以实现不同段地址也能获得相同物理地址的效果。
其物理地址将被存储在相应的寄存器中。
例如对于数据段,将其段地址存放在DS中。用mov/add/sub等访问内存单元的指令时,CPU就将我们定义的数据段中的内容当做数据来访问;
对于代码段,将其段地址存放在CS中,将端中第一条指令的偏移地址放在IP中,这样CPU就能执行我们定义的代码段中的指令;
对于栈段,将其段地址存放在SS中,将栈顶的单元偏移地址存放在SP中,这样CPU在需要进行栈操作的时候(push/pop),就将我们定义的栈段当做栈空间来使用。

由此可见,无论我们怎样安排,CPU将内存中某段内容当做什么,都取决于我们让什么寄存器保存了该段的数据。
如下图所示,表示了几种不同的寻址方式【1】p114。寻址是整个程序运行的基础。
计算机是如何运行的:从CPU到内存管理_第14张图片

再说数据发送

解决了寻址问题后,意味着可以找到想要读写的内存区域了,那么就需要一个寄存器来保存将要从内存中读取的数据。(待续)

再说控制发送

代码要想运行,无非是需要控制和数据两个条件。即:我要对什么东西,做什么操作。下面需要考虑控制是如何收发的。
CPU的控制包括了逻辑运算、跳转操作。(待续)

。。。

80386CPU为例

寄存器在硬件角度是如何实现的

寄存器如何与内存配合使用

CPU与栈

栈实际上是一种通用的数据结构,并非只在CPU中使用。在解决实际问题的时候,我们经常需要来描述先进后出的情况,这就是栈的存在意义。
再思考得深入一点,为什么我们需要栈结构呢?
我们都知道,数据是存储在内存中(硬盘中的数据要想使用也必须先读取到内存中),而内存的寻址方式一般都是基础地址+偏移的方式。对于一个结构体来说,其指针都是指向结构体的开始位置,要想访问内部的变量,需要将该指针进行移动。这是一种灵活的设计方式,可以随心所欲地进行访问,但要想描述先进后出的情况,这样的方式还是太灵活了一点。
想一想,如果有多个数据A、B、C依次被存储,然后按照C、B、A的顺序进行读取。
这里实际上涉及到了两点,一点是这个结构有了记忆功能,后面存储的值没有覆盖前面存储的值;另一点就是取出的顺序刚好和放入的顺序相反。
如果按照直接存储的方式的话,如下图所示:
| A | B | C |
创建的时候我们分别有了指向ABC起始地址的引用。要想描述ABC的位置,我们分别需要ABC三个变量的引用(我们不能保证ABC能够在内存中连续存储,否则也能按照偏移来获得其地址)。那么要想实现栈的效果,我们就需要同时使用ABC三个变量来描述。
这还是三个变量,若有一万个,也得这么做。
我们只想实现一个先进后出的效果,我们希望通过一个变量就能对栈顶元素进行插入或删除,这就是栈的来源:我们将实现隐藏到内部,对外可以通过一个变量进行实现。
回到CPU这里。

为什么需要栈结构?

运行一个程序的时候,CPU如何使用栈进行函数调用/参数传递?内存如何划分开代码区/数据区/堆栈区,CPU如何感知?CPU如何运作?

CPU的栈的实现

8086使用SS:SP两个寄存器来指向栈顶元素。每当Push和Pop的时候,通过SS:SP的指针来移动指向不同内存,从而实现栈的效果。
需要使用一个新的寄存器AX来存放数据,而不能直接与内存进行数据交换。

CPU push的实现
  1. SP=SP-2,SS:SP指向当前栈顶前面的内存单元,以当前栈顶前面的单元作为新的栈顶;
  2. 将AX中的内容SS:SP指向的内存单元处,SS:SP此时指向新栈顶。

8086CPU中,入栈时,栈顶从高地址向低地址方向增长。
push、pop实际上是一种内存传送指令

CPU pop的实现
  1. 将SS:SP指向的内存单元出的数据送入AX中;
  2. SP=SP+2,SS:SP指向当前的栈顶下面的单元,以当前栈顶下面的单元作为新的栈顶。

为什么内存需要段结构?内存段具体是怎么划分的?CPU如何对段内的数据进行寻址的?

栈段的分类

在编程时,可以将一组内存单元定义为一个段(这只是一种安排,CPU并不会由于这种安排,就在执行Push、Pop等栈操作指令是就自动将我们定义的栈段当做栈空间来使用,而是需要将SS:SP指向我们定义的栈段才可以)。
内存的段是逻辑的,并非物理的。
我们可以将一段内存定义为一个段,用一个段地址表示段,用偏移地址来表示段内的单元。
一般来说,我们将一个段划分为如下几个部分:

数据段

用于存放数据。将其段地址放在DS中,用mov/add/sub等访问内存单元的指令时,CPU就将我们定义的数据段中的内容当做数据来访问。

代码段

将其段地址放在CS中,将断种第一条指令的偏移地址放在IP中,这样CPU就将执行我们定义的代码段中的指令。

堆栈段

将其段地址放在SS中,将栈顶单元的偏移地址放在SP中,这样CPU在执行栈操作的时候如Push/pop,就将我们定义的栈段当做栈空间来使用。

不管我们怎样安排,CPU将内存中的数据当做数据/指令、栈,全部是我们自己定义的,内存本身只是一种通用的存储介质,并不能做类型的区分。
CPU将内存中的某段内容当做代码,是因为CS:IP指向了哪里;CPU将某段内存当做栈,是因为SS:SP指向了那里。CPU将内存中的某段内容当做数据,是因为DS指向了那里。

到后面详细学习进程相关的时候就可以发现在现代的操作系统中,一个进程地址空间中将分为进程控制块、数据段、代码段、堆栈段。后面详细讨论。

段寄存器与段起始地址的给出

五、程序如何运行

程序的完整流程

编辑 -> .asm -> 编译(masm) -> .obj -> 连接(Link) -> .exe -> 加载(Command) -> 内存中的程序 -> 运行(CPU)

计算机是如何运行的:从CPU到内存管理_第15张图片
计算机是如何运行的:从CPU到内存管理_第16张图片

是谁调用了程序?程序结束后返回到了哪里?

任何操作系统都需要对外提供一个称为shell(外壳)的程序,用户使用这个程序来操作计算机进行工作。
如果用户要执行一个程序,则输入该程序的可执行文件的名称。shell会首先根据文件名找到可执行文件,然后将这个可执行文件加入内存,设置CS:IP指向程序的入口。此后,shell会暂停运行,CPU运行程序。程序运行结束后,返回到shell中。
当然以上是简单一讲,具体还要涉及到中断等操作,将在后面逐步深化。

根据什么设置CPU的CS:IP指向程序的第一条要执行的命令

是由可执行文件中的描述信息指明的。可执行文件由描述信息和程序组成。程序来自于源程序中的汇编指令和定义的数据,描述信息则主要是编译、连接程序对源程序中相关伪指令进行处理所得到的信息。
我们用伪指令end描述了程序的结束和城市的入口。在编译、连接后,由end start指明的程序入口,被转化为一个入口地址,存储在可执行文件的描述信息中。当程序被加载入内存之后,加载者从程序的可执行文件的描述信息中读到程序的入口地址,设置CS:IP,这样CPU就从我们希望的地址中开始执行。
如下图所示:
计算机是如何运行的:从CPU到内存管理_第17张图片

在代码段中使用栈

可通过手动创建数据的方式来实现申请内存的效果,然后将其当做一个栈空间。
疑问:程序如何自动使用栈?

将数据、代码、栈放入不同的段
为什么要这么做:
  1. 规范。将其进行分类有利于寻址;
  2. 容量。若三者加起来大于最大限制,则无法存放。
如何将其放入不同的段中:

通过汇编来定义不同的段,使用不同的段名做区分。而通过段名就可以获得段地址,从而可以对其进行引用。段内位置即指定即可。
而对于数据、代码和栈的定义只是逻辑定义,此处写出并非CPU就当其为相应的数据结构了,而是通过指定其地址到相应寄存器来实现的。
计算机是如何运行的:从CPU到内存管理_第18张图片##### 指令的转移:call和return

进程这一概念的发展

进程需要解决什么问题

进程的管理

进程的组成部分

进程控制块

进程控制块如何进行内存寻址

进程控制块如何将内存中的数据传入CPU

进程在内存中的存放形式

CPU如何继续下一步操作

举一个程序运行的例子,从在内存中的存储形式、CPU在内存中的寻址、分段/分页 、寄存器的使用等角度进行考虑

List item


多个进程的调度

两状态模型与五状态模型

竞态:锁

什么是竞态?什么是锁?为什么需要锁?
什么是锁?为什么会出现死锁
如何避免死锁

进程与线程

进程与线程的关系

线程间通信的方法

共享什么资源,如何共享


三、中断

什么是中断?为什么出现了中断的概念?

一种通用CPU都需要具有的能力,可以在执行完当前正在执行的指令之后,检测到从CPU外部发送过来的或内部产生的一种特殊信息,并且立即对所接收到的信息进行处理。这种特殊的信息被称为中断信息。

为什么发展出了中断概念?在有这个概念之前,计算机是如何运行的?

前提1:南北桥运行速度的差异

前提2:I/O设备处理的速度远慢于CPU处理的速度,造成CPU时间的浪费

前提3:其他异常的处理(除0、键盘输入、屏幕输出。。)

在中断产生之前,CPU就是按照程序中所编号的代码来执行的。将程序载入内存,设置相关寄存器指向代码,依次执行。

中断是由什么产生?

按照产生的起源,中断分为内中断与外中断。

都有些什么类型的中断?如何将其按照类型表示?

既然类型有很多种,就需要一张表来表示。此处引入中断向量表的概念。
中断向量表就是中断向量的列表。什么是中断向量呢?实际上就是中断处理程序的入口地址。可以通过该地址来找到中断处理程序。
中断向量表在内存的固定位置保存(8086在0000:0000到0000:03FF保存),存放了256个中断源所对应的中断处理程序的入口(即起始地址)。CPU只需知道中断码,就能通过中断向量表的相应表项来得到中断处理程序。

TODO:
中断向量表图示:

中断程序是如何被触发的?

此处便是指:如何收到中断信息。

中断的硬件实现?

如上文所述,CPU之所以能够知道内存中哪一部分数据为程序,是因为我们让CS:IP寄存器指向了保存程序的内存地址。同理,要实现中断,实际上我们要做的也是将CS:IP指向中断处理程序的入口。一旦这样,CPU就开始执行中断处理程序。

中断如何保存CPU中的现场,保存在哪里?如何恢复现场?需要如何进行寻址?

因为在处理完中断程序后我们还需要还原之前的程序,因此我们需要将中断前的现场保留下来。需要保存什么才能还原一个程序的现场呢?实际上就是各种寄存器指针。
我们通过栈来保存。(终于用上CPU的栈了,详情请见上文)
具体流程如下所示:

  1. 8086CPU收到中断信息
  2. 从中断信息中取得中断类型码
  3. 将标志寄存器的值入栈。(因为中断过程中需要改变标志寄存器的值,因此需要首先将其保存起来)
  4. 设置标志寄存器的第8位TF和第9位IF的值位0。(为什么?)
  5. 将CS的值入栈保留。
  6. 将IP的值入栈保留;
  7. 将内存地址为中断类型码4和中断类型码4+2的两个字单元中读取中断处理程序的入口地址,设置IP和CS;
  8. CPU开始执行由程序员编写的中断处理程序。

举一个中断的例子

参考文献

[1] 深入理解计算机系统(第三版)

你可能感兴趣的:(计算机系统)