中断和异常
中断和异常都是程序执行过程中的强制性转移,转移到相应的处理程序。
中断通常因为硬件而发生,用来处理处理器外部的事件,?比如外围设备的请求。
异常通常在处理器执行指令过程中检测到错误时发生,比如除零, 页错误等
什么是中断
在实模式下,使用的是?BIOS?中断。BIOS?中断是由系统提供的使用?BIOS?的一组功能。中断存在中断向量表中。
在保护模式下,?BIOS?中断不能使用。原来的中断向量表被?IDT(中断描?述符表)?代替。
IDT
IDT?中的描述符有三种
? ??????中断门描述符(int?调用)
? ??????陷阱门描述符(call?调用)
? ??????任务门描述符(不常用,?Linux?根本没用)
IDT?的作用是将每一个中断向量和一个描述符对应起来。
中断产生有两种情况
? ???????硬件产生的外部中断
? ???????intn
intn?类型调用使用?IDT?寻找中断处理程序,类似调用门的使用
外部中断:?要建立硬件中断和中断向量号之间的对应关系
? ?????不可屏蔽中断(NMI?引脚接收,于?IF?是否被设置无关,对应中断向?量号为?2)
? ?????可屏蔽中断(INTR?接收,中断和向量号的关系由可编程中断控制器 8259 A?建立)
什么是异常
异常的三种类型
? ??????Fault:可更正的异常,?处理完返回到原指令
????????功能
????????? ?????根据优先级在同时发生的中断设备中选择应该处理的请求
????????? ?????通过对其寄存器的设置来屏蔽或打开相应中断?结构和工作原理
????????结构:两片级联的?8259A?与?CPU?相连,共挂十五个外设。
????????工作:
????????主?8259A?对应的端口地址是?20?h?和?21?h
????????从?8259A?对应的端口地址是 A0?h ?和?A1?h
????????设置?8259A:通过向相应的端口写入特定的?ICW
?
ICW??格式
分析
ICW?1 ?触发模式,选择中断向量种类,选择?8259A?架构
ICW?2?决定对应哪个中断向量
ICW 3?决定了传进哪个外设中断
ICW 4??正常为?01?h
写入?ICW?2?时, IRQ 0~?IRQ 7?对应?20?h ~?27?h;同理?IRQ 8 ~?IRQ?15?对应?28?h?~?2?F?h
io_delay 函数:延迟函数,等待操作完成
314 ~?322?屏蔽了所有的外部中断,此时写入的是?OCW
使用?OCW?的两种情况
???????屏蔽或打开外部中断
? ??????发送?EOI?给?8259A?通知它中断处理结束
屏蔽或打开外部中断,只需往?8259A?写入?OCW?1?即可
OCW?1 被写入中断屏蔽寄存器(IMR)
发送?EOI 给?8259A 通过往?20 h ?或 A0 h?写?OCW 2 来实现的
mov ?al ????, ?20h
out?20h/A0h,?al
如何建立IDT,如何实现一个自定义的中断
建立?IDT
IDT?段
/*
255 个描述符完全相同
SpuriousHandler : 在屏幕右上角打印红色"!",然后死循环
*/
// SpuriousHandler?实现
//?加载IDTR
实现一个中断
/*
修改IDT ?--->?将80h?中断单独列出来?---> 新增一个函数来处理中断?UserIntHandler??在末尾iretd?指令返回
*/
新增一个函数
UserIntHandler?实现
3.??时钟中断例程
pmtest9.asm?部分代码展示
时钟中断处理程序
_ClockHandler:
ClockHandler???equ _ClockHandler - $$
inc byte?[gs:((80 * 0?+ 70) *?2)]??;?屏幕第?0?行, 第?70?列。
mov al,?20h
out 20h, al????????????;?发送?EOI
iretd
打开时钟中断
;mov???al, 11111111b ??; 屏蔽主?8259?所有中断
mov al, 11111110b ??; 仅仅开启定时器中断
out 021h, al???; 主?8259, OCW1.
call???io_delay
mov al, 11111111b ??; 屏蔽从?8259?所有中断
out 0A1h, al???; 从?8259, OCW1.
call???io_delay
IDT?表的修改
LABEL_IDT:
; 门????????????????????????目标选择子,??????????偏移,?DCount,?属性
%rep?32
Gate SelectorCode32, SpuriousHandler, 0, DA_386IGate
%endrep
.020h: Gate SelectorCode32, ClockHandler, 0, DA_386IGate
%rep 95
Gate SelectorCode32, SpuriousHandler, 0, DA_386IGate
%endrep
.080h: Gate SelectorCode32, UserIntHandler, 0, DA_386IGate
IdtLen equ $ - LABEL_IDT
IdtPtr dw IdtLen - 1 ; 段界限
dd 0 ; 基地址
; END?of?[SECTION?.idt]
开中断
call??Init8259A
int 080h
sti
jmp $
代码调试调试
在时钟中断处理程序?ClockHandler?中插入?Magic?Break:
_ClockHandler:
ClockHandler???equ _ClockHandler - $$
xchg bx,?bx
inc byte?[gs:((80 * 0?+ 70) *?2)]??;?屏幕第?0?行, 第?70?列。
mov al,?20h
out 20h, al????????????;?发送?EOI
iretd
调试截图展示如下:
尽管没有使用?int?指令直接跳转,因为外中断的特性,程序进入了设置好的?02h?中断,将?int 80h?设置的字符进行变化。??实验结果展示如下:
4. ??自定义的中断向量
自定义中断功能
int?80h?中断
功能说明:初始化时间字符串"20yy/mm/dd hh:mm:ss"为当前时间,?并输出在屏幕上
int 20h?中断(时间中断)
功能说明:将屏幕上已输出的时间字符串更新为当前时间
部分代码展示
int?80h?中断
LABEL_DATA:
_szTime db "20yy/mm/dd hh:mm:ss", 0
szTime equ _szTime - $$
DataLen equ $ - LABEL_DATA
; END of [SECTION .data1]
...
_UserIntHandler:
UserIntHandler equ _UserIntHandler - $$
xor eax, eax
xor ebx, ebx
xor esi, esi
xor edi, edi
mov esi, szTime ; 源数据偏移
mov al, 9 ; 年
out 70h, al
in al, 71h
mov bl, al ; 十位
shr bl, 4
and al, 0fh ; 个位
mov ah, bl
add ax, 03030h ; 转换为 ascii 码
mov [esi + 2], ah ; 赋值
mov [esi + 3], al
mov al, 8 ; 月
out 70h, al
in al, 71h
mov bl, al ; 十位
shr bl, 4
and al, 0fh ; 个位
mov ah, bl
add ax, 03030h ; 转换为 ascii 码
mov [esi + 5], ah ; 赋值
mov [esi + 6], al
mov al, 7 ; 日
out 70h, al
in al, 71h
mov bl, al ; 十位
shr bl, 4
and al, 0fh ; 个位
mov ah, bl
add ax, 03030h ; 转换为 ascii 码
mov [esi + 8], ah ; 赋值
mov [esi + 9], al
mov al, 4 ; 时
out 70h, al
in al, 71h
mov bl, al ; 十位
shr bl, 4
and al, 0fh ; 个位
mov ah, bl
add ax, 03030h ; 转换为 ascii 码
mov [esi + 11], ah ; 赋值
mov [esi + 12], al
mov al, 2 ; 分
out 70h, al
in al, 71h
mov bl, al ; 十位
shr bl, 4
and al, 0fh ; 个位
mov ah, bl
add ax, 03030h ; 转换为 ascii 码
mov [esi + 14], ah ; 赋值
mov [esi + 15], al
mov al, 0 ; 秒
out 70h, al
in al, 71h
mov bl, al ; 十位
shr bl, 4
and al, 0fh ; 个位
mov ah, bl
add ax, 03030h ; 转换为 ascii 码
mov [esi + 17], ah ; 赋值
mov [esi + 18], al
; 下面显示一个字符串
mov ah, 0Ch ; 0000: 黑底 1100: 红字
mov edi, (80 * 8 + 0) * 2 ; 目的数据偏移。屏幕第 8 行, 第 0 列。
cld
.1:
lodsb
test al, al
jz .2
mov [gs:edi], ax
add edi, 2
jmp .1
.2: ; 显示完毕
iretd
int?20h?时间中断
_ClockHandler:
ClockHandler???equ _ClockHandler - $$
xor eax, eax
xor ebx, ebx
mov al, ?9????????????????????; 年
out 70h, al
in ?al, ?71h
mov bl, ?al???????????????????;?十位
shr?bl,?4
and al, ?0fh ??????????????????; 个位
mov ah, ?bl
add ax,?03030h ???????????????; 转换为?ascii?码
mov?[gs:((80 * 8 + 2) *?2)],?ah?;?更改屏幕显示
mov?[gs:((80 * 8 +?3)?*?2)],?al
mov al, ?8????????????????????;?月
out 70h, al
in ?al, ?71h
mov bl, ?al???????????????????;?十位
shr?bl,?4
and al, ?0fh ??????????????????; 个位
mov ah, ?bl
add ax,?03030h ???????????????; 转换为?ascii?码
mov?[gs:((80 * 8 + 5)?*?2)], ah?;?更改屏幕显示
mov?[gs:((80 * 8 +?6)?*?2)],?al
mov al, ?7????????????????????;?日
out 70h, al
in ?al, ?71h
mov bl, ?al???????????????????; 十位
shr?bl,?4
and al, ?0fh ??????????????????; 个位
mov ah, ?bl
add ax,?03030h ???????????????; 转换为?ascii?码
mov?[gs:((80 * 8 + 8) *?2)], ah?;?更改屏幕显示
mov?[gs:((80 * 8 + 9)?*?2)],?al
mov al, ?4 ????????????????????; 时
out 70h, al
in ?al, ?71h
mov bl, ?al???????????????????;?十位
shr?bl,?4
and al, ?0fh ??????????????????; 个位
mov ah, ?bl
add ax,?03030h ???????????????; 转换为?ascii?码
mov [gs:((80 *?8 +?11)?*?2)],?ah;?更改屏幕显示
mov?[gs:((80 * 8 +?12)?*?2)], al
mov al,?2????????????????????;?分
out 70h, al
in ?al, ?71h
mov bl, ?al???????????????????;?十位
shr?bl,?4
and al, ?0fh ??????????????????; 个位
mov ah, ?bl
add ax,?03030h ???????????????; 转换为?ascii?码
mov [gs:((80 *?8 +?14)?*?2)],?ah;?更改屏幕显示
mov?[gs:((80 * 8 +?15)?*?2)], al
mov al, ?0????????????????????; 秒
out 70h, al
in ?al, ?71h
mov bl, ?al???????????????????;?十位
shr?bl,?4
and al, ?0fh ??????????????????; 个位
mov ah, ?bl
add ax,?03030h ???????????????; 转换为?ascii?码
mov [gs:((80 *?8 +?17)?*?2)],?ah;?更改屏幕显示
mov?[gs:((80 * 8 +?18)?*?2)], al
mov al,?20h
out 20h, al???????????????????;?发送?EOI
iretd
中断调用
; 下面显示一个字符串
push???szPMMessage
call???DispStr
add????esp, 4
; 调用中断向量
int????080h
sti
jmp????$
实验结果展示
成功输出当前时间,?且不断进行更新。
=========================================================================
1. ??什么是中断,什么是异常?
中断包括外部中断和来自于指令?int?n?的中断(n?即向量号),其中外部中断又分为不可?屏蔽中断(NMI?引脚接收,于?IF?是否被设置无关,对应中断向量号为?2)和可屏蔽中断(INTR?接收, 中断和向量号的关系由可编程中断控制器 8259A ?建立)。中断是由硬件设备产生的,?从物理上说就是电信号,?它们通过中断控制器(8259A)发送给?CPU,接着?CPU 判断收到的?中断来自于哪个硬件设备(定义在内核中),最后由?CPU?发送给内核, 由内核处理中断。
异常包括?Fault?、Trap 和?Abort,分别对应出错、陷入和编程异常。出错保存的?EIP 指向?触发异常的那条指令,也就是说当从异常返回时, 出错会重新执行那条指令,如缺页异常就?是是一种出错,?所以当缺页异常处理完成之后,还会去尝试重新执行那条触发异常的指令;??陷入保存的?EIP 指向触发异常的那条指令的下一条指令,陷入不会重新执行因为异常的指令;?可编程中断可由编程者用 int ?指令来触发,和陷入类似,从此类异常返回时也是返回到触发?异常的下一条指令。
中断和异常的相同点在于: 最后都是由?CPU?发送给内核,?由内核去处理;处理程序的流?程设计上是相似的。两者的不同点在于:?产生源不相同, 异常是由?CPU 产生的,?而中断是由?硬件设备产生的; 中断是异步的,这意味着中断可能随时到来; 而异常是?CPU?产生的, 所以是时钟同步的; 当处理中断时, 处于中断上下文中;处理异常时, 处于进程上下文中。?
2. ??8259A?的工作原理是怎样的?怎么给这些中断号的处理向量初始化值?
8259A?的结构如下图所示, 它的功能是根据优先级在同时发生的中断设备中选择应该处?理的请求,并通过对其寄存器的设置来屏蔽或打开相应中断。
通过初始化编程向?8259A?写入相应的初始化命令?ICW,可以使芯片处于一个规定的基本?工作方式,?并在此方式下进行工作。8259A?的初始化命令字共有?4 个?ICW1-ICW4,进行初始?化时要求 ICW1-ICW4 ?按如下图所示的顺序写入。其中,?ICW1?触发模式,选择中断向量种?类,选择?8259A 架构;ICW2?决定对应哪个中断向量;ICW3?决定了传进哪个外设中断;?ICW4?正常为?01h。
当?8259A?开始工作时,一个外部中断请求信号会首先通过中断请求线?IRQ?传输到?IMR (中断屏蔽寄存器),IMR 根据所设定的?OCW1,决定是将其丢弃还是接受。使用?OCW?的两?种情况,一种是用于屏蔽或打开外部中断,另一种发送?EOI 给?8259A 通知它中断处理结束,?这里使用的是前者。
如果可以接受,则?8259A 将?IRR(中断请求暂存寄存器)中代表此?IRQ?的位置?1,以表?示此?IRQ 有中断请求信号,并同时向?CPU?的?INTR(中断请求)管脚发送一个信号,在等待?CPU?转到中断服务的过程中可能有其余的?IRQ?线送来中断请求,这些请求都会接受?IMR?的?挑选, 如果没有被屏蔽,?那么这些请求也会被放到?IRR ?中,也即 IRR?中代表它们的?IRQ?的?相应位会被置?1。当?CPU 执行完一条指令时后, 会检查一下?INTR 管脚是否有信号,如果发?现有信号, 就会转到中断服务, 此时,CPU 会立即向?8259A?芯片的?INTA(中断应答)?管脚?发送一个信号。当芯片收到此信号后,判优部件开始工作,它在?IRR?中,挑选优先级最高的?中断, 将中断请求送到?ISR(中断服务寄存器),也即将?ISR?中代表此?IRQ?的位置位,并将?IRR?中相应位置?0,表明此中断正在接受?CPU?的处理。同时,将它的编号写入中断向量寄存?器?IVR?的低三位(IVR?是由?ICW2?所指定的)。这时,CPU?还会送来第二个?INTA?信号,当?收到此信号后, 芯片将?IVR?中的内容, 也就是此中断的中断号送上通向?CPU?的数据线。
3. ??如何建立?IDT,如何实现一个自定义的中断?
1)将?IDT 放入一个单独的段中, 定义了?IDT 所对应的中断向量的偏移。
2)定义偏移定义
上图展现的是时钟中断处理程序。其功能为在时钟中断到来时,将[gs:((80 * 0?+ 70)*2]位?置的字符的值增?1。
3)调用中断,?开中断
4. ??如何控制时钟中断,?为什么时钟中断的时候没有看到?int 指令?
控制时钟中断的方法就是在?IDT 表中的中断向量号单独写出来,对应一个中断处理的函?数,在函数里定义时钟中断的处理过程, 比如此例中就单独定义了?020h?中断和?080h?中断:
没有看到?int?的原因是我们在?289?行进行了开中断,时钟中断是外部中断,?每次中断都?会通过?IRQ?中断请求线来请求中断, 不需要?int 调用。
5. ??简要解释一下?IOPL?的作用与基本机理
(1)IOPL?的作用
保护模式通过?IOPL?和?I/O?许可位图来限制用户进程进行的?I/O?操作。 IOPL?是?I/O?保护机制的?关键之一,位于寄存器?eflags?的第?12?、13?位。指令?in?、ins?、out?、outs?、cli?、sti?只有在?CPL??<=?IOPL?时才能执行。这些指令被称为?I/O?敏感指令(I/O Sensitive?Instructions)。如果低特权?级的指令试图访问这些?I/O?敏感指令将会导致常规保护错误(#GP)。
(2)IOPL?的运行机理
I/O?位图基址是一个以?TSS?的地址为基址的偏移,?指向的便是?I/O?许可位图。之所以叫做位?图,是因为它的每一位表示一个字节的端口地址是否可用。如果某一位为?0,则表示此位对?应的端口号可用,为?1?则不可用。由于每一个任务都可以有单独的?TSS,所以每一个任务可?以有它单独的?I/O?许可位图。
以教材例子演示:
由于?I/O?许可位图开始有?12?字节内容为?0FFh,即有?12×8=96?位被置为?1,所以从端口?00h?到?5Fh?共?96?个端口地址对此任务不可用。同理,接下来的?1?字节只有第?1?位(从?0?开始数)?是?0,表示这一位对应的端口(61h)可用。如果?I/O?位图基址大于或等于?TSS?段界限,就表示没有?I/O?许可位图, 如果?CPLIOPL,则所有?I/O?指令都会引起异常。 I/O?许可位?图的使用使得即便在同一特权级下不同的任务也可以有不同的?I/O?访问权限。