热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

intelcpu中的中断

中断(Interrupt)包括中断和异常两种类型,异常通常由CPU上执行的指令直接触发,而中断是由外设发出的电信号触发的,但是那么是否所有的外设都直接接在CPU的中断PIN脚上触发中

    中断(Interrupt)包括中断和异常两种类型,异常通常由CPU上执行的指令直接触发,而中断是由外设发出的电信号触发的,但是那么是否所有的外设都直接接在CPU的中断PIN脚上触发中断?CPU有多少负责中断额PIN脚?CPU如何区别可屏蔽中断和非可屏蔽中断?CPU如何区别Faults、Traps、Aborts?本篇文章主要来搞懂这些问题。

一、中断控制器

    首先,CPU肯定不会为允许所有的外设都直接接在其上(否则要总线干什么),即使是通知发生中断这一种功能。应该有专门的中间设备/元器件负责这个工作,这个中间设备就是中断控制器。我刚刚学习中断的时候,甚至都不清楚中断控制器是个硬件还是软件,是否是CPU的一部分,那么这个地方先给出两个中断控制器的外观图片:

Intel-P8259Aich10

82093aa

    以上三张图片分别是Intel的IP8259A芯片、intel的ICH10南桥芯片以及Intel的S82093AA芯片,他们都有中断控制器的功能,其中8259A芯片比较老式,目前已经基本淘汰;Intel平台流行的做法是高级可编程将中断控制器(APIC)集成到南桥芯片(I/O Controller Hub)中,而较老的S82093AA芯片是最初的APIC的形态。

    可编程控制器(Programmable Interrupt Controller)是通常由两片 8259A 风格的外部芯片以“级联”的方式连接在一起。每个芯片可处理多达 8 个不同的 中断请求。因为从 PIC 的 INT 输出线连接到主 PIC 的 IRQ2 引脚,所以可用 IRQ 线的个数达到 15 个,如下图所示(示意图,并不代表一定这么连接):

8259A

    8259A除了起到向CPU引入多个外部中断源的作用外,还有一些基本功能,如中断分级、中断屏蔽,中断管理提(存储中断向量)等。

    随着SMP架构的发展,Intel在2000年左右的时候率先引入一种名为 高级可编程控制器的新组件(Advanced Programmable Interrupt Controller),来替代老式的 8259A 可编程中断控制器。APIC包括两部分:一是“本地 APIC(Local APIC)”,主要负责传递中断信号到指定的处理器,本地APIC通常集成到CPU内部,之所以成为Local,是相对CPU而言。另外一个重要的部分是 I/O APIC,主要是收集来自 I/O 设备的 Interrupt 信号且将中断时发送信号到本地 APIC。

    每个本地 APIC 都有 32 位的寄存器,一个内部时钟,一个本地定时设备以及为本地中断保留的两条额外的 IRQ 线 LINT0 和 LINT1。所有本地 APIC 都连接到 I/O APIC,形成一个多级 APIC 系统,如下图所示(示意图,并不代表一定这么连接):

ioapic

    当然,本地APIC除了接收来自IO APIC的中断信号,还可以接收其他来源的中断,比如接在CPU LINT0和LINT1管脚上的中断、IPI中断(核间中断)、APIC定时器产生中断、性能监视计数器中断、热传感器中断、APIC内部错误中断等。无论是PIC还是APIC,都通过某种方式与CPU相连(有的时候并不直接相连),这解决两个问题:

(1)CPU对多个外设的中断的管理

(2)多CPU的中断管理(APIC)

    当然,APIC自有一套硬件逻辑去实现这些功能,Intel也提供了相关的用户手册,这里就不深究了,有兴趣可以参考资料3——Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 3 Chapter 10 advanced programmable interrupt controller(APIC).

    以下为ULK中关于IRQ线以及中断控制器的工作逻辑的描述:

    每个能够发出中断请求的硬件设备控制器都有一条名为IRQ(Interrupt ReQuest)的输出线。所有现有的IRQ线都与PIC的硬件段路的输入引脚相连,PIC执行下列动作:

1、监视IRQ线,检查产生的信号。如果有两条或两条以上的IRQ线产生信号,就选择引脚编号较小的IRQ线。

2、如果一个引发信号出现在IRQ线上:

  • a.把接收到的引发信号转换成对应的向量。
  • b.把这个向量存放在中断控制器的一个I/O端口,从而允许CPU通过数据总线读此向量。
  • c.把引发信号发送哦哦嗯到处理器的INTR引脚,产生一个中断。
  • d.等待,知道CPU通过把这个中断信号写进PIC的一个I/O端口来确认它;当这种情况发生时,清INTR线。

3、返回到第一步。

二、Intel x86 CPU中断管脚

    APIC系统主要作用是管理外设产生的异步中断,而对Intel x86架构下的各种异常,如故障、陷阱以及终止,系统是如何管理的那?这得看Intel手册:Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 3 的Chaper 2 system architecture overview 和Chapter 6 interrupt and exception以及Chapter 10 APIC。

    Intel x86架构提供LINT0和LINT1两个中断引脚,他们通常与Local APIC相连,用于接收Local APIC传递的中断信号,另外,当Local APIC被禁用的时候,LINT0和LINT1即被配置为INTR和NMI管脚,即外部IO中断管脚和非屏蔽中断管脚。INTR引脚负责向处理器通知发生了外部中断,处理器从系统总线上读出由外部中断控制器提供的中断向量(Interrupt Vector)号,例如从8259a提供。NMI中断使用2号中断向量。

    通常一个INTR引脚能够接收64个中断源,至于CPU内部怎么通过LINT0和LINT1进行中断的处理,就不管了,抑或还有其他的中断引脚什么的...

三、Intel x86架构对中断的支持——中断向量和IDT表

    Intel采用中断向量表(Interrupt Describtor Table)的方式去管理中断中断 。对于中断、陷阱、故障以及终止,有一些是Intel自己在设计CPU架构的时候就能够预知的,例如在执行除零时就会出现异常,在页式管理机制就可能出现缺页异常,有一些是Intel无法预估的,比如一个未来设计的设备产生的中断。对于前者,Intel称之为Intel 64 and IA-32 architectures for architecture-defined exceptions and interrupts,即Intel64和IA-32架构定义的架构相关的异常和中断,姑且简称为架构相关的中断和异常。

    为了区别这些中断和异常,Intel给出了中断向量(Interrupt Vector),即使用一个数字代表一个特殊的中断或者异常类型,Intel规定中断向量号的范围是0~255,0~31号为架构相关的异常和中断,32~255为User Defined Interrupt(保护模式):

    中断向量是中断向量表(IDT)的索引,而中断向量表存在于内存的某个位置,由Intel的寄存器IDTR负责记录其基址(线性地址)和大小。IDT表中包含了操作系统中注册的外部IO中断的处理程序的入口地址,以及其他操作系统实现的架构相关的中断和异常的处理函数入口地址(这些地址又存放在所谓的gate destribtor中)。INTR和IDT的关系如下图所示:

IDTRandIDT

    上图中的Gate for Interrupt #n实际上分为三类:task gate,interrupt gate,trap gate,结构如下图所示:

gate_descriptor

    gate在Linux内核(3.11.1)中数据结构如下(32bits):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct desc_struct {
union {
struct {
unsigned int a;
unsigned int b;
};
struct {
u16 limit0;
u16 base0;
unsigned base1: 8, type: 4, s: 1, dpl: 2, p: 1;
unsigned limit: 4, avl: 1, l: 1, d: 1, g: 1, base2: 8;
};
};
} __attribute__((packed));

64bits的gate describtor:

1
2
3
4
5
6
7
8
9
/* 16byte gate */
struct gate_struct64 {
u16 offset_low;
u16 segment;
unsigned ist : 3, zero0 : 5, type : 5, dpl : 2, p : 1;
u16 offset_middle;
u32 offset_high;
u32 zero1;
} __attribute__((packed));

    IDT中的Gate可以以某种方式获取到具体的中断/异常处理函数的入口地址,从而可以执行该中断/异常处理函数:

interrupt_procedure_call

    GDT和LDT分别为全局描述符表(Global Destribtor Table)和本地描述附表(Local Destribtor Table),其中存储的内容为整个操作系统对各segment的描述,GDT、LDT、segment牵扯到内存管理的机制将在《Linux Kernel内存管理》系列中被具体学习,此处关心gate整个概念。

四、中断过程

    PIC和APIC是用于解决多设备中断管理问题的硬件设备,而APIC还可以解决SMP架构中断管理的问题。PIC通常指两片8259a级联,而APIC通常包括两个部分Local APIC和IO APIC,Local APIC通常集成在CPU内部(Intel),外部与IO APIC相连,内部与CPU管脚LINT0和LINT1相连。

    Intel 64和IA-32架构CPU对外提供中断向量和中断描述符表(IDT)的机制来处理中断和异常。中断按照下列逻辑触发并被执行(以键盘为例):

1、用户按下键盘按键;

2、电平信号变化通知中断控制器发生了一次中断;

3、中断控制器通知CPU此次中断的中断向量号;

4、CPU判定是否要处理此次中断,如果要处理,转5,否则退出;

5、从IDTR寄存器读取IDT的基址+中断向量号,找到对应的中断处理函数入口地址;

6、CPU按照某种软件策略执行该中断处理函数;

五、中断硬件处理细节(IA32/Intel 64)

对于第四节中断过程的步骤4、步骤5的一些细节,描述如下(摘自ULK):

当执行了一条指令后,cs和eip这对寄存器包含了下一条要执行的指令的逻辑地址。在处理那条指令之前,控制单元会检查在运行前一条指令时是否存在已经发生了一个中断或者异常。如果发生了一个中断或一场,那么控制单元执行下列操作:

1、确定与中断或异常关联的向量i(i的范围为0~255)。

2、读由idtr寄存器指向的IDT表中的第i项(在下面的描述中,我们假定IDT表项中包含的是一个中断门或一个陷阱门)。

3、从gdtr寄存器获得GDT的基地址,并在GDT中查找,以读取IDT表项中的选择符标识的段描述符。这个描述符指定中断或异常处理程序所在段的基地址。

4、确信中断是授权的(中断)发生源发出的。首先将当前特权级CPL(存放在cs寄存器的低两位)与段描述符(存放在GDT中)的描述符特权级DPL比较,如果CPL小于DPL,就产生一个“General Protection”异常,因为中断处理程序的特权不能低于引起中断的程序的特权。对于编程异常,则做进一步的安全检查:比较CPL与处于IDT中的门描述符的DPL,如果DPL小于CPL,就产生一个“General Protection”异常。这最后一个检查可以避免用户程序访问特殊的陷阱门或中断门。

5、检查是否发生了特权级的变化,也就是说,CPL是否不同于所选择的段描述符的DPL。如果是,控制单元必须开始使用与新的特权级相关的栈。通过执行以下步骤来做到这点:

  • a.读tr寄存器,以访问运行进程的TSS段。
  • b.用与新特权级相关的栈段和桟指针的正确值装载ss和esp寄存器。这些值可以在TSS中找到。
  • c.在新的栈中保存ss和esp以前的值,这些值定义了与旧特权级相关的栈的逻辑地址。

6、如果发生的是“故障(Fault)”,用引起异常的指令地址装载cs和eip寄存器,从而使得这条指令能再次被执行。

7、在栈中保存eflag、cs及eip的内容。

8、如果硬件产生了一个错误码,则将它保存在栈中。

9、装载cs和eip寄存器,其值分别是IDT表中的第i项门描述符的段选择符和偏移量字段。这些值给出了中断或者异常处理程序的第一条指令的逻辑地址。

控制单元所执行的最后一步就是跳转到中断或异常处理程序。还句话说,处理完中断信号后,控制单元所执行的指令就是被选中处理程序的第一条指令。

中断或异常被处理后,相应的处理程序必须产生一条iret指令,把控制权转交给被中断的进程,这将迫使控制单元:

1、用保存的栈中的值状态cs、eip或eflag寄存器。如果一个硬件出错码曾被压入栈中,并且在eip内容的上面,那么,执行iret指令前必须先弹出这个硬件出错码。

2、检查处理程序的CPL是否等于cs中的最低两位的值(这意味着被中断的进程与处理程序运行在同一特权级)。如果是,iret终止执行;否则,转入下一步。

3、从栈中装载ss和esp寄存器,因此,返回到与旧特权级相关的栈。

4、检查ds、es、fs以及gs寄存器的内容,如果其中一个寄存器包含的选择符是一个段描述符,并且DPL值小于CPL,那么,清相应的段寄存器。控制单元这么做是为了禁止用户态的程序(CPL=3)利用内核以前所用的段寄存器(DPL=0)。如果不清这个寄存器,怀有恶意的用户态程序就能利用他们来访问内核地址空间。

存疑

Q1:对门(gate)的概念一直比较含糊,computer sicence + gate,让人很容易直接反应到门电路——实现与、或、非等逻辑功能的电路,显然是个硬件。但是在Intel 64和IA-32手册中,门是这样被描述的:

The architecture also defines a set of special descriptors called gates (call gates, interrupt gates, trap gates, and task gates). These provide protected gateways to system procedures and handlers that may operate at a different privilege level than application programs and most procedures. For example, a CALL to a call gate can provide access to a procedure in a code segment that is at the same or a numerically lower privilege level ( more privileged) than the current code segment. To access a procedure through a call gate, the calling procedure1 supplies the selector for the call gate. The processor then performs an access rights check on the call gate, comparing the CPL with the privilege level of the call gate and the destination code segment pointed to by the call gate.

架构同时定义了一些列的被称作门的描述符(call门、interrupt门、trap门、task门)。这些描述符向系统程序和处理函数提供了保护性的网关,这可以让他们运行在与大多数应用程序不同的权限级别上。例如,通过call gate的一次调用,能够向代码段程序同时或周期性的提供比当前的代码段更低权限数值(更多的权限)。要通过call gate调用一个程序,调用者需要提供当前call gate的selector,接着,处理器就提供了再当前call gate上校验过的权限,通过比较当前call gate的CPL的权限级别和通过当前call gate指向目标代码段的权限。

那么,在Intel架构中,门是一个虚拟的概念,而不是一个硬件,是一个描述符,是Intel对外提供的用于某些特殊权限需要时的程序调用,系统程序向CPU提供门描述符(包含程序代码入口),而CPU根据权限判定是否允许执行。之所以分为这么多门,是因为权限的不同。不知道理解的对不对哈。

Q2:关于栈的切换的疑问。引发中断的程序的优先级不能高于中断处理程序的优先级,否则引发异常,这好理解,但是一旦优先级发生变化时,进行堆栈的切换过程,有些迷惑,主要是对栈的理解:我理解栈是一段连续的内存空间,方便从C程序转换为CPU指令的执行(主要是函数嵌套调用,过程调用),由于运行程序的权限不同,分为内核栈和用户栈,每个进程都有自己的栈,但中断处理函数并没有自己的栈,而是与进程的内核栈共享。中断处理函数在内核启动后,即被装载在属于内核段的某处内存中,要被压入具体的进程的内核栈?cs和eip被设置后,压栈是自动的?慢不慢?类似与call?普通函数调用libc影射的方式?混乱..


推荐阅读
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了重温Linux内核:互斥和同步相关的知识,希望对你有一定的参考价值。文章目录 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • AMD专用内存是什么意思 AMD专用内存与普通内存的区别是什么
    相信一些用户在网上在购买内存的时候找到一些低价位的内存,看描述上写有“amd专用内存”,amd专用内存是什么意思?那么对于这种称为amd专用内存的不难理解, ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • 服务器上的操作系统有哪些,如何选择适合的操作系统?
    本文介绍了服务器上常见的操作系统,包括系统盘镜像、数据盘镜像和整机镜像的数量。同时,还介绍了共享镜像的限制和使用方法。此外,还提供了关于华为云服务的帮助中心,其中包括产品简介、价格说明、购买指南、用户指南、API参考、最佳实践、常见问题和视频帮助等技术文档。对于裸金属服务器的远程登录,本文介绍了使用密钥对登录的方法,并提供了部分操作系统配置示例。最后,还提到了SUSE云耀云服务器的特点和快速搭建方法。 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • RouterOS 5.16软路由安装图解教程
    本文介绍了如何安装RouterOS 5.16软路由系统,包括系统要求、安装步骤和登录方式。同时提供了详细的图解教程,方便读者进行操作。 ... [详细]
  • Java 11相对于Java 8,OptaPlanner性能提升有多大?
    本文通过基准测试比较了Java 11和Java 8对OptaPlanner的性能提升。测试结果表明,在相同的硬件环境下,Java 11相对于Java 8在垃圾回收方面表现更好,从而提升了OptaPlanner的性能。 ... [详细]
  • 超级计算机 26010,全球最强超级计算机搭载的SW26010处理器解析
    全球最强超级计算机神威·太湖之光搭载的申威26010处理器每片处理器包含4个核心,片上的4个核心通过片上网络互联,并通过PCI-E3.0对外连接,每个核心拥有独立的128位DDR3 ... [详细]
author-avatar
红糖里有砂
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有