操作系统运行环境与运行机制(中断篇)

处理器状态

CPU由运算器,控制器,一系列寄存器组成

其中寄存器主要有两类寄存器

用户可见寄存器高级语言编译器通过优化算法分配并使用之,以减少程序访问内存次数

控制和状态寄存器:用于控制处理器的操作,通常由OS代码使用

 

控制和状态寄存器作用:

①用于控制处理器操作

②在某种特权级别下可以访问,修改

常见的控制和状态寄存器:PC,IR,PSW(程序状态字,记录处理器运行状态如条件码,模式,控制位)

 

操作系统能信任的程序只有内核函数,所以就要求保护,这就需要硬件提供基本的运行机制:

1.处理器具有特权级别,能在不同的特权运行不同的指令集合

2.硬件机制可将OS与用户程序隔离

 

现代处理器通常设计成多种状态,在PSW寄存器中设置1位,根据运行程序对资源和指令的使用权从而设置不同CPU状态

例子:

在EFLAG寄存器中IOPL位,I/O特权标志用两位二进制位来表示,该字段指定了要求执行I/O指令的特权级。如果当前的特权级别在数值上小于等于IOPL的值,那么,该I/O指令可执行(IOPL值越小,权限越大),否则将发生一个保护异常。

 

操作系统只需要两种CPU状态:内核态:运行OS程序,用户态:运行用户程序,同时这两种状态可执行两种不同指令集合,前者对应特权指令,只给OS使用,后者对应非特权指令,只给用户程序使用

 

访管指令(内核态又叫管理态)属于非特权指令,但是执行该指令可以将CPU从用户态陷入内核态

 

X86处理器支持4个特权处理级别

R0,R1,R2,R3(R0内核态----->R3用户态),不同级别能运行的指令不同,但是目前x86CPU只用R0,R3两个级别

 

变化方式:

用户态-->内核态:唯一途径:中断/异常机制

内核态-->用户态:设置程序状态字PSW

 

其中有个陷入需要执行陷入指令(访管指令),它是提供给用户程序接口,用来调用OS功能:

例如Linux中的int,trap,syscall,sysenter、sysexit等

 

 

中断/异常机制

 

可以这么说OS是由中断驱动的,它的作用:

1.及时处理设备发来的中断请求
2.可使OS捕获用户程序提出的服务请求
3.防止用户程序执行过程中的破坏性活动等等

 

为什么引入中断和异常?

中断引入:为了CPU与设备的并行操作

当CPU启动设备进行输入/输出后,设备便可以独立工作,CPU转去处理与此次输入/输出不相关的事情,此时CPU与I/O设备是并行操作;当设备完成输入/输出后,通过向CPU发中断信号,报告此次输入/输出的结果,让CPU决定如何处理以后的事情

 

异常引入:表示CPU执行指令是本身出现的问题,如算术溢出、除零、取数时的奇偶错,访存地址时越界或执行了“陷入指令”(系统调用) 等,这时硬件改变了CPU当前的执行流程,转到相应的错误处理程序或异常处理程序或执行系统调用

 

可以得出:

中断:外部事件,正在运行的程序所不期望的
异常:由正在执行的指令引发

 

 中断/异常举例:

 

总结

ps.陷入指令即为访管指令

1.中断被称为异步中断,异常为同步中断。

为什么分成同异步呢?

前者是由硬件随机产生,可以在指令执行期间,而后者是由CPU控制器产生,只有在一条指令执行完毕后,CPU才会发出中断信号,而在执行期间不会。

 

2.从中断的来源来说,来自外部的外设所产生的中断,叫做硬件中断;而从CPU内部所执行的(类似于80x86中的INT)指令所产生的中断,成为软件中断,是软件代码所产生的中断,此处就是指陷阱。

 

中断/异常工作原理:

硬件做了什么事?

捕获中断源发出的中断/异常请求,以一定方式响应,将处理器控制权交给特定中断处理程序(中断响应: 发现中断、接收中断的过程)

软件做了什么事?

识别中断/异常类型完成相应的处理

 

其中处理器控制部件中设有中断寄存器

 

中断响应过程:

 

中断码:也叫中断号,系统为每一个注册的中断源都会分配一个唯一编号用于识别该中断,一般来说,计算机有16个中断线与各种需要用中断方式工作的不同外设相连(每个中断线有一个标号也就是中断号),当一条中断线被激活后,CPU就会立即停下当前的工作。

中断向量一个内存单元,存放中断处理程序入口地址程序运行时所需的处理机状态字

中断向量表:通过中断号找到中断向量的一张表,系统刚引导时,内存0x00000到0x0003FF共1KB的空间用于存放中断向量表

 

下图为Linux中断向量表:

 

PS.根据其中断的性质,是否可以被屏蔽掉,而分为可屏蔽中断和非可屏蔽中断

 


其中中断号位128(d)也为0x80(H)用于系统调用的可编程异常

所以总的中断流程为:

1.设备控制器或其他系统硬件产生一个中断发给中断控制器(它可以接收多个外部中断源的中断请求,并进行优先级判断,选中当前优先级最高的中断请求,并将此请求送到CPU的INTR端),如果可以再发送给处理器

2.处理器得到中断信号结束当前指令执行,然后发送应答信号

3.处理器将上下文的PSW和PC寄存器内容压入控制栈,保存现场

4.根据中断号(每条中断线对应一个IRQ编号)查中断向量表,将中断处理程序入口地址PC寄存器中

5.保存CPU中通用寄存器内容到内核栈

6.执行中断处理程序(该程序是设备驱动程序(是用于对设备进行管理的内核代码)一部分)

7.中断处理程序执行完成后,将控制栈中数据依次恢复到各通用寄存器

8.恢复PSW和PC值

 

对于中断处理程序:

I/O操作正常结束
若有程序正等待此次I/O的结果,则应将其唤醒
若要继续I/O操作,需要准备好数据重新启动I/O


I/O操作出现错误
需要重新执行失败的I/O操作
重试次数有上限,达到时系统将判定硬件故障

 

对于多个中断情况:

①执行一个中断时,禁止其他中断,即使有中断也会被挂起,等到当前中断处理程序结束,但在恢复用户程序之前,允许中断,由CPU检测是否还有挂起的中断

缺点:未考虑相对优先级和时间限制要求 

②定义中断优先级,交给中断控制器判断,允许高优先级打断低优先级进行,然后低优先级被保存现场,然后控制权交给高优先级中断处理程序

 

中断处理程序(Interrupt Handler)和中断服务例程ISR(Inerrupt Service Routine)

首先中断处理程序(Interrupt Handler)和中断服务例程ISR(Inerrupt Service Routine)是两个不同的概念,简单来说就是,一条中断线对应一个中断处理程序,而一个中断处理程序再对应若干个中断服务例程。

从上图中能很直观的看出所有的中断服务例程挂在中断请求队列中,这个工作是由request_irq()函数来完成的,其实也就是对中断服务例程进行注册,而中断处理程序就相当于某个中断向量的的处理程序,比如上图中IRQ0x08_interrupt()是中断号为9的总处理程序,假如这个9号中断由5个设备共享,那么这5个设备都分别有其对应的中断服务例程(也就是一条中断线被多个相似设备共享),此时会调用handle_IRQ_event()来调用ISR。

 

IRQn_interrupt表示从IRQ0x00_interrupt到IRQ0x0f_interrupt(即上图中断号从0-15)的任意一个中断处理程序。这个中断处理程序需要调用do_IRQ()函数,而do_IRQ()函数对收到的中断请求进行应答,并禁止这条中断线,(中断处理程序不会被反复调用即程序A中不会再调用程序A,因为对应的中断线被禁止了),然后要确保这条中断线上有一个有效的中断服务例程,而且目前这个这个中断服务例程已经启动但未执行。这时do_IRQ()调用handle_IRQ_event()来运行挂在这条中断线上的所有中断服务例程

 

X86处理器对中断/异常支持

 

X86的四种模式:

实模式,保护模式,SMM模式和虚拟8086模式

实模式:为了兼容早期的8086十六位机器的一种模式,80386一加电启动后就处于这种模式,在这种状态下软件可以访问的物理内存空间不能超过1MB(它的32位地址线只使用了低20位),而且无法发挥Intel80386以上32位CPU的4GB内存管理能力,因为寻址空间只有16位

保护模式:寻址空间发生了变化,它从16位或者说1M空间变成了32位寻址空间,有4GB的寻址空间;它提供分页和分段机制这两种机制能够让不同软件放在不同的特权级,访问不同空间且相互之间是隔离的,这种不同的机制就可以使得我们应用程序可以运行在较低的级别,它被限制在运行在一个有限的空间里面,不会破坏操作系统 ,不会访问那些特权指令

 

实模式:中断向量表 (Interrupt Vector)

存放中断服务程序的入口地址
  1. 入口地址=段地址左移4位+偏移地址
  2.不支持CPU运行状态切换
  3.中断处理与一般的过程调用相似

保护模式:中断描述符表 (Interrupt Descriptor Table)

采用门(gate) 描述符数据结构表示中断向量

 

 

四种类型门描述符
1.任务门(Task Gate)
2.中断门(Interrupt Gate)
       i.给出段选择符 (Segment Selector)、中断/异常程序的段内偏移量 (Offset)
       ii.通过中断门后系统会自动禁止中断
3.陷阱门(Trap Gate):与中断门类似,但通过陷阱门后系统不会自动禁止中断
4.调用门(Call Gate)

 

X86下如何找到中断处理程序入口地址:

最终x86详细过程

1.确定与中断/异常关联向量i

2.通过IDTR寄存器找到IDT表,获得中断描述符(表中第i项:中断号)                                     PS.中断描述符包含:中断门,任务门,陷阱门(用于系统调用)

任务门(task gate) 当中断信号发生时,必须取代当前进程的那个进程的TSS选择符存放在任务门中。门的DPL字段为0
中断门(interruptgate) 包含段选择符和中断或异常处理程序的段内偏移量.当控制权转移到一个适当的段时,处理器清IF标志,从而关闭将来会发生的可屏蔽中断.门的DPL字段为0
陷阱门(Trap gate) 与中断门相似,只是控制权传递到一个适当的段时处理器不修改IF标志,即不会禁止中断例如可以fork()期间再fork().门的DPL字段为3

3.通过GDTR寄存器找到GDT地址,结合之前找到的中断描述符表中的段选择符,选择GDT表中对应的段描述符,从该段描述符得到中断/异常处理程序基地址

4.特权级检查(X86使用了两个特权级R0内核态,R3用户态,不同级别对应着不同的指令集合)

5.检查是否发生了特权级的变化(从用户态R3切换到内核态R0),如果是,则进行堆栈切换(必须使用与新的特权级相关的栈,内核态使用的堆栈与用户态使用的堆栈是不同的)

6.硬件内容压内核栈中,保存上下文环境(保存现场,压入PSW和PC和通用寄存器中内容);如果异常产生了硬件出错码,也将它保存在栈中

7.如果是中断,清IF位IF(bit 9) [Interrupt enable flag]  该标志用于控制处理器对可屏蔽中断请求(maskable interrupt requests)的响应。置1以响应可屏蔽中断,反之则禁止可屏蔽中断(就是禁止中断))

8.通过中断描述符中的段内偏移量段描述符中的基地址,找到中断/异常处理程序的入口地址,执行其第一条指令

 

 

作者水平有限,文章肯定有错还请各位指点!!!感谢!!!

 

 参考链接:

http://blog.csdn.net/jn1158359135/article/details/7761011

https://books.google.com.sg/books?id=COk5iWux6B0C&pg=PA105&lpg=PA105&dq=%E5%A4%96%E4%B8%AD%E6%96%AD&source=bl&ots=Z02VXuYGIy&sig=mTBPra5Y6Th-txP5sIbLNnz15qE&hl=zh-CN&sa=X&ved=0ahUKEwiI1YeTjP7UAhVSv5QKHUe_CFcQ6AEIOzAD#v=onepage&q=%E5%A4%96%E4%B8%AD%E6%96%AD&f=false

posted on 2017-07-10 17:23  chaunceyctx  阅读(1759)  评论(0编辑  收藏  举报

导航