x86架构学习之中断

前言

读本文前,你需要了解x86的操作模式、寻址方式、特权级相关知识,具体参考这篇文章

中断与异常

打断cpu当前任务的事件都叫中断,根据是否可屏蔽分为:

  • 可屏蔽中断,即可通过软件配置相关寄存器(通常是PIC中的某个寄存器)来开启和关闭。
  • 不可屏蔽中断,即中断不可关闭,一般用在一些非常紧急或灾难性的事件上。

根据中断的来源,我们将中断分为外部中断内部异常

外部中断

简称中断,后文如果没特别说明,中断都指外部中断,由外部IO事件触发,由于时钟源来自CPU外部,与CPU时钟不同源,因此是异步事件。
外部中断,一般是由CPU的外部引脚电信号触发,这些引脚留给硬件厂商去定义中断,例如主板厂商可以将USB中断挂载70号中断引脚上,MCU厂商可以将片上外设(SPI、I2C等)的中断挂载在指定中断线上。

内部异常

由CPU内部事件触发,触发时钟源与CPU时钟同源,因此是同步事件。

  • 处理器探测异常
    • 故障,可修复,修复后可正常运行,如0除数、计算结果溢出、缺页等。故障发生时,入栈作为故障处理函数返回地址的,是故障指令所在的地址,而不是下一条指令的地址(这是一般中断和异常的行为),以便修复故障后,指令能够再次被执行,如缺页。
    • 陷阱,通过软件插入断点指令触发。
    • 异常终止,应用级灾难,只能终止当前任务。
  • 编程异常,即通过软件插入中断或异常(INT n)指令来触发,由于需要显示的中断指令,因此,被称为软中断,利用软中断,可以模拟任意外部中断信号,在操作系统,软中断多用于系统调用。
  • 机器检测异常,硬件上检测到错误时,会触发的异常,这个异常属于纯硬件故障,不同的版本的x86架构实现不同,因此不存在兼容性。(可以理解为硬件bug检测机制,硬件的设计不可能完美)

中断号

x86的中断号总共有256个,其中前32个(0~31)中断号已经被CPU硬件预定义或保留,软件无法修改,这32个中断号也是异常,后224个中断号可由硬件厂商自由配置使用。

中断号异常类型简称描述触发源
0故障#DEDivide错误使用 DIV and IDIV 指令
1陷阱#DBDebug任意代码和数据访问都有可能
2故障NMI Interrupt不可屏蔽中断
3陷阱#BPBreakpointINT3 指令.
4陷阱#OFOverflowINTO 指令.
5故障#BRBOUND Range ExceededBOUND 指令.
6故障#UDInvalid Opcode (Undefined Opcode)执行无效指令
7故障#NMDevice Not Available (No Math Coprocessor)Floating-point or WAIT/FWAIT 指令
8故障异常终止#DFDouble Fault
9故障异常终止#MFCoProcessor Segment Overrun (reserved)
10故障#TSInvalid TSS硬件任务切换时,TSS段无效
11故障#NPSegment Not Present访问不存在的段
12故障#SSStack Segment Fault非法栈操作,ss寄存器加载无效值
13故障#GPGeneral Protection各种权限校验不通过
14故障#PFPage Fault访问无效页
15保留
16故障#MF浮点运算错误 (数学错误,math fault)浮点运算相关指令
17故障#ACAlignment Check非对齐访问内存
18异常终止#MCMachine CheckError codes (if any) and source are model dependent
19故障#XMSIMD Floating-Point ExceptionSIMD 浮点指令错误使用
20#VEVirtualization ExceptionEPT violations5
21#CPControl Protection ExceptionThe RET, IRET, RSTORSSP, and SETSSBSY instructions can generate this exception. When CET indirect branch tracking is enabled, this exception can be generated due to a missing ENDBRANCH instruction at the target of an indirect call or jump.
22-31保留
32-255可屏蔽中断外部中断引脚触发 或 INT n 指令触发

中断描述符表(interrupt descriptor table,IDT)

中断描述符主要用于说明该中断的入口地址,中断描述符表则是将中断描述符一项一项管理在一个数组中,每一项中断描述符占用8个字节(64位模式下,占用16字节),其下标索引对应中断号
描述符主要字段如下:

描述符特权级描述符类型16位段选择符16位偏移

中断描述符表的地址存放在cpu的idtr寄存器中,idtr由专门的指令lidt指令进行修改 。

idtr寄存器包含48位,结构如下,32位的基地址和16的大小。
在这里插入图片描述

门描述符

描述符中有一个专门的字段type,用于标志该描述符的类型,该字段占用5位,可以表示多达32种描述符,它可以表示段描述符门描述符,以及具体是什么段描述符、什么门描述符。对于门描述符,它被用于在不同特权级之间穿越,门描述符 目前有四种,任务门、中断门、陷阱门、调用门,被中断(描述符表)用到的描述符类型有3种:

  • 任务门,用于硬件任务切换,16位段选择符指向下一个任务的任务状态段(TSS),16位偏移被忽略,TSS中存放有下一个任务的cs和ip寄存器的值。任务状态段描述符(TSSD)放在全局描述符表中(GDT),考虑到兼容性,Linux并没有使用x86的硬件任务。
  • 中断门,由16位段选择符+16位偏移指定中断入口地址,执行中断程序前,会关中断(设置EFLAGS寄存器IF标志为0)
  • 陷阱门,同中断门相同,但是不关中断。

3种门描述符详细结构如图
在这里插入图片描述
64位机的门描述符结构如图
在这里插入图片描述

ps: Linux利用中断门处理中断,利用陷阱门处理异常。

由门描述符找到中断程序入口

中断描述符表中存放的门描述符,其中有两个字段用于定位中断入口地址:段选择符和偏移。先通过段选择符,在全局描述符表(GDT)中找到段描述符,然后从段描述符中取出段基地址,再将基地址加上偏移得到中断程序入口的线性地址(这里涉及分段、分页概念,具体参考x86内存寻址)。
在这里插入图片描述

中断硬件行为

中断调用

  • 当一个中断(异常)发生时,硬件根据其中断号,在idtr指向的中断描述符表中,找到对应索引的中断描述符。
  • 如果中断描述符是中断门或陷阱门,则
    • 如果中断由外部中断或处理器探测异常触发,则检查当前cs的RPL(即CPL)与门描述符指定的段描述符的DPL的大小关系,如果 C P L > D P L 段 CPL>DPL_{段} CPL>DPL,则触发异常(中断不能降权),如果 C P L = D P L 段 CPL=DPL_{段} CPL=DPL,说明特权级没有发生变化,则不需要更换堆栈,直接在原栈上保存ip、cs、eflags,如果 C P L > D P L 段 CPL>DPL_{段} CPL>DPL,说明特权提高了,需要更换堆栈,根据tr段寄存器找到任务状态段(TSS),在TSS中提取中断程序的ss和sp寄存器值(栈段和栈指针,由软件预先写入),得到新栈,然后在新栈中保存当前的ss、sp、ip、cs、eflags,最后将新的ss和sp值加载到对应寄存器。
    • 如果中断由编程异常触发,则还多两步操作:检查段描述符前,还需检查门描述符,判断,触发异常的程序是否有权限访问该门,即 C P L ≤ D P L 门 CPL≤DPL_{门} CPLDPL,这一步是防止用户态程序通过int n访问中断门提权。最后,如果异常生成了错误码(部分异常会产生错误吗,用于指示出错原因),则需要在新栈上压入错误码。
      在这里插入图片描述
    • 根据门描述符及其指定的段描述符,找到中断入口地址(段+偏移),加载到cs和ip寄存器,开始执行中断处理程序。
  • 如果中断描述符是任务门,处理流程与上面差不多,只不过,TSS不再通过tr寄存器获取,而是通过任务门描述符中的任务段选择符字段获取。

中断返回

中断返回通过iret指令进行,返回过程是调用过程的逆过程。

IA-32e操作模式下的中断行为

之前说的中断行为都是IA-32操作模式下的行为,即32位机和仅使用32位模式的64位机,具体操作模式的知识,请参考前言中提到的文章。IA-32e模式即64位机相对于32位机扩展出来的模式,也称为长模式。长模式下,中断行为与IA-32有主要如下不同:

  • 中断行为中操作的多数寄存器扩展为64位
  • 中断描述符扩展成16字节,其中增加的ist字段,用于栈切换。因为长模式下,CPU非常有限的使用段寄存器,ss中的选择符被无视,IA-32操作模式我们知道,新的ss和sp是从TSS中获取的,但是现在不再是,ss选择符被强制为空,RPL被设置成CPL。
  • 描述符中多出一个ist字段,这个字段是长模式为了特殊中断(NMI, double-fault, and machine-check)设计的,当然你可用于其他中断。当中断发生并且需要换栈时,如果ist为0,则行为和以前一样,否则,ist表示下标,指向TSS中的中断栈表(IST)项,cpu会将这个表项的指针加载到RSP寄存器。

可编程中断控制器

可编程中断控制器(programmable interrupt controller,PIC),它的主要用途有如下:

  • 可设置中断的优先级1。两个中断事件同时出现时,高优先级的优先得到响应。高优先级会抢占低优先级,产生中断嵌套。同优先级中断不会抢占自己。
  • 中断重定向。可以将外部设备的中断信号重新挂载到不同的CPU中断线上(对应不同中断号)
  • 中断屏蔽。可暂时屏蔽不需要的中断。
  • 中断分发2。对于多核CPU,每个cpu存在一个本地APIC,A代表高级(advanced),并且还存在一个总APIC,总APIC会根据各本地APIC中断的繁忙程度,进行调度分发中断。

  1. 抛开APIC来讲,CPU是没有中断优先级概念的,任何中断向量都可以打断前一中断向量。 ↩︎

  2. 说到中断分发,还小有意思,本地APIC中会有一个寄存器(PPR,只读),用于表示当前CPU的优先级,PPR由两部分决定,一个是当前正在处理的中段的优先级(ISRV),一个是当前任务优先级(TPR寄存器),TPR由操作系统更新。总APIC会优先将中断分发给低优先级的CPU,如果多个CPU共享同优先级,就需要用到本地APIC的另一个寄存器,仲裁寄存器,每当本地APIC接受一个中断,则寄存器值降为0,否则加1,总APIC会优先将中断分发给高仲裁值的本地APIC。 ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值