中断(APIC)

中断是硬件和软件交互的一种机制,可以说整个操作系统,整个架构都是由中断来驱动的。一个中断的起末会经历设备,中断控制器,CPU 三个阶段:设备产生中断信号,中断控制器翻译信号,CPU 来实际处理信号

中断控制器前面的文章曾讲过一些 PIC,PIC 只用于单处理器,对于如今的多核多处理器时代,PIC 无能为力,所以出现了更高级的中断控制器 APIC,APIC 分成两部分 LAPIC 和 IOAPIC,前者 LAPIC 位于 CPU 内部,每个 CPU 都有一个 LAPIC,后者 IOAPIC 与外设相连。外设发出的中断信号经过 IOAPIC 处理之后发送某个或多个 LAPIC,再由 LAPIC 决定是否交由 CPU 进行实际的中断处理。p6 家族处理器的 APIC 总体的示意图如下:

图片说明

可以看出每个 CPU 上有一个 LAPIC,IOAPIC 是一系统芯片组,各个中断消息通过总线发送接收。关于 APIC 的内容很多也很复杂,详细描述的可以参考 intel 手册,本文不探讨其中的细节,只在上层较为抽象的层面讲述,理清 APIC 模式下中断的过程。

IOAPIC

IOAPIC 主要负责接收外部的硬件中断,将硬件产生的中断信号翻译成具有一定格式的消息,然后通过总线将消息发送给一个或者多个 LAPIC。IOAPIC 主要组成如下:

  • 24 个中断管脚,一个 IOAPIC 支持 24 个中断
  • 一张 24 项的中断重定向表(PRT,Programmable Redirection Table),每个表项都是一个 64 位的寄存器
  • 一些可编程寄存器,例如窗口寄存器,版本寄存器等等
  • 通过 APIC 总线发送和接收 APIC 信息的一个信息单元

重定向表项 RTE(Redirection Table Entry)

了解 IOAPIC 的工作,最重要的就是了解重定向表项/寄存器,每个管脚都对应着一个 64 位的重定向表项,来具体看看这 64 位代表的具体信息:

图片说明
图片说明
图片说明

这个表项/寄存器包含了该中断的所有属性信息,以什么方式触发中断,传送的方式状态,管脚极性等等,这张图来自 interrupt in linux ,对各个字段的说明应该很清楚了就不再赘述,说几点:

  • destination field 和 destination mode 字段决定了该中断发送给哪个或哪些 LAPIC

  • vector,中断控制器很重要的一项工作就是将中断信号翻译成中断向量,这个中断向量就是 IDT 的索引,IDT 里面的中断描述符就存放着中断处理程序的地址。**在 PIC 中,,而在 APIC 模式下,IRQ 对应的 vecotr 由操作系统对 IOAPIC 初始化的时候设置分配**。

  • IOAPIC 的管脚没有优先级之分,不像 PIC 的 IRQ0 的优先级比 IRQ1 的优先级要高,而 IOAPIC 对中断优先级的区分在于管脚对应的重定向表项的 vector 字段。

由上,对 IOAPIC 的工作总结:当 IOAPIC 的管脚接收到外设发来的中断信号后,根据相应的重定向表项格式化出一条中断消息,然后发送给 destination field 字段列出的 LAPIC

LAPIC

LAPIC 要比 IOAPIC 复杂的多,其主要功能是接收中断消息然后交由 CPU 处理,再者就是自身也能作为中断源产生中断发送给自身或其他 CPU。所以其实 LAPIC 能够收到三个来源的中断:

  • 本地中断:时钟,温度监测等
  • 外部中断:IOAPIC 发来的
  • IPI:处理器间中断,其他 LAPIC 发来的

inel 手册里面做了更精细复杂的分类,私以为了解这三大类就行了。了解 LAPIC 从它的一些重要寄存器入手,通过这些寄存器的作用来了解 LAPIC 如何工作的:

主要寄存器

IRR(Interrupt Request Register)

中断请求寄存器,256 位,每位代表着一个中断。当某个中断消息发来时,如果该中断没有被屏蔽,则将 IRR 对应的 bit 置 1,表示收到了该中断请求但 CPU 还未处理

ISR(In Service Register)

服务中寄存器,256 位,每位代表着一个中断。当 IRR 中某个中断请求发送个 CPU 时,ISR 对应的 bit 上便置 1,表示 CPU 正在处理该中断

EOI(End of Interrupt)

中断结束寄存器,32 位,写 EOI 表示中断处理完成。写 EOI 寄存器会导致 LAPIC 清理 ISR 的对应 bit,对于 level 触发的中断,还会向所有的 IOAPIC 发送 EOI 消息,通告中断处理已经完成。

ID

用来唯一标识一个 LAPIC,LAPIC 与 CPU 一一对应,所以也用 LAPIC ID 来标识 CPU

TPR(Task Priority Register)

任务优先级寄存器,确定当前 CPU 能够处理什么优先级别的中断,CPU 只处理比 TPR 中级别更高的中断。比它低的中断暂时屏蔽掉,也就是在 IRR 中继续等到

另外 ,vector 为每个中断对应的中断向量号。

PPR(Processor Priority Register)

处理器优先级寄存器,表示当前正处理的中断的优先级,以此来决定处于 IRR 中的中断是否发送给 CPU。处于 IRR 中的中断只有优先级高于处理器优先级才会被发送给处理器。PPR 的值为 ISR 中正服务的最高优先级中断和 TPR 两者之间选取优先级较大的,所以 TPR 就是靠间接控制 PPR 来实现暂时屏蔽比 TPR 优先级小的中断的。

SVR(Spurious Interrupt Vector Register)

可以通过设置这个寄存器来使 APIC 工作,原话 To enable the APIC。

ICR(Interrupt Command Register)

中断指令寄存器,当一个 CPU 想把中断发送给另一个 CPU 时,就在 ICR 中填写相应的中断向量和目标 LAPIC 标识,然后通过总线向目标 LAPIC 发送消息。ICR 寄存器的字段和 IOAPIC 重定向表项较为相似,都有 destination field, delivery mode, destination mode, level 等等。

下面为有关本地中断的一些寄存器

本地中断

LAPIC 本身还能作为中断源产生中断,LVT(Local Vector Table) 就是自身作为中断源的一个配置表,总共 7 项(不同架构下可能不同),每项 32 位,同 IOAPIC,每一项也是一个寄存器,如下所示:

图片说明

其中时钟中断,中断源为 APIC 的时钟。

温度探测中断,中断源为 探测 CPU 温度的小探头,用来监测处理器温度,查了查 intel 手册,有两个 Monitor,作用分别如下,英文翻译不准,直接放原文了:

TM1 controls the processor’s temperature by modulating the duty cycle of the processor clock.

TM2 controls the core temperature of the processor by reducing the operating frequency and voltage of the processor and offers a higher performance level for a given level of power reduction than TM1.

其他几个中断有兴趣的可以查阅 intel 64 and IA-32 那个开发手册,卷3 10.5。

LAPIC总结

对上面 LAPIC 的寄存有一定了解后,对 LAPIC 的工作应该也有一定了解了,对于从 IOAPIC 发来的中断消息,首先判断自己是否接收这个消息,这要根据重定向表项中的 destination field,destination mode 来判断:

destination mode 为 0 时表示物理模式,destination field 字段表示一个 APIC ID,LAPIC 根据 ID 寄存器比对判断是否由自己来接收

destination mode 为 1 时表示逻辑模式,LAPIC 需要另外两个寄存器 LDR 和 DFR 来辅助判断,具体判断方式很复杂,逻辑模式分为 flat 和 cluster,cluster 又分为 flat cluster 和 hierachical cluster,了解就好,感兴趣的参考 interrupt in linux 1.2.3,有着很详细的讲解,这里不赘述。

判断不该自己接收就忽略,否则 IRR,ISR,TPR,PPR,EOI 等寄存器配合使用来决定发送中断到 CPU 进行处理,此外如果中断类型为 NMI 等特殊中断是直接发送给 CPU 进行处理,不需要上述步骤。

要实现处理器间中断,一个处理器想把中断发送给另一个处理器时,就在 ICR 中填写相应的中断向量和目标 LAPIC 标识,然后通过总线向目标 LAPIC 发送消息。而 LAPIC 有着自己的时钟,用来计数产生时钟中断,还有温度探测,来控制 CPU 的温度。

最后来看一张 intel 手册中给出的 LAPIC 结构图,对其各个部分直接的协同工作有个认识,如下所示:

图片说明

APIC 中断过程

有了上述的了解,来看中断在 APIC 阶段的过程:

  1. IOAPIC 根据 PRT 表将中断信号翻译成中断消息,然后发送给 destination field 字段列出的 LAPIC
  2. LAPIC 根据消息中的 destination mode,destination field,自身的寄存器 ID,LDR,DFR 来判断自己是否接收该中断消息,不是则忽略
  3. 如果该中断是 SMI/NMI/INIT/ExtINT/SIPI,直接送 CPU 执行,因为这些中断都是负责特殊的系统管理任务。否则的话将 IRR 相应的位置 1。
  4. 如果该中断的优先级高于当前 CPU 正在执行的中断,而且当前 CPU 没有屏蔽中断的话,则中断当前正处理的中断,先处理该高优先级中断,否则等待
  5. 准备处理下一个中断时,从 IRR 中挑选优先级最大的中断,相应位置 0,ISR 相应位置 1,然后送 CPU 执行。
  6. 中断处理完成后写 EOI 表示中断处理已经完成,写 EOI 导致 ISR 相应位置 0,对于 level 触发的中断,还会向所有的 I/O APIC 发送 EOI 消息,通知中断处理已经完成。

访问 APIC

IOAPIC 有两个寄存器映射到内存:IOREGSEL,地址为 FEC0 xy00h;IOWIN,地址为 FEC0 xy10h。IOREGSEL 用来指定要读写的寄存器,然后从 IOWIN 中读写。

LAPIC 的寄存器有很多,比如 IRR,ISR 等等,也是采用内存映射到地址 0xFEE00xxx。

因此访问和配置 APIC 直接可以对相应的内存进行读写即可。而所谓内存映射,就是把这些寄存器看作内存的一部分,读写内存,就是读写寄存器,可以用访问内存的指令比如 mov 来访问寄存器还有一种是 IO端口映射,这种映射方式是将外设的寄存器(也叫端口)看成一个独立的地址空间,访问这片空间不能用访问内存的指令,而需要专门的 in/out 指令来访问

不论是内存映射还是端口映射,所能映射的范围其实很小,比如 x86 的 IO 空间只有 64KB,但实际上外设的寄存器有很多,不可能全部映射,所以基本上由很多外设硬件都采取这种方式来访问所有的寄存器:通过一个寄存器来选择该硬件的其他寄存器,然后通过另一个寄存器来实际读写选择的寄存器。就像将硬件封装出一个窗口,干什么事情向这个窗***代,实际的事情则由硬件内部自行处理。

而有关 APIC 配置初始化方面就是利用上述所说的访问方式对 IOAPIC 和 LAPIC 进行配置,设置相关的一些寄存器值。光是设置好这两项还没完,因为 APIC 适用多核的处理器,对于处理器也要进行相应的配置才能与多核处理器协同工作。

这两方面的配置都有相应文档参考,前者是 intel 的开发手册,后面为 MP spec,有兴趣的可以看看。关于这方面的系统源码 linux 的确是比较复杂,可以先参考参考 xv6,xv6 也是一个关于多核的操作系统,里面有对 APIC,MP 的配置初始化操作,可以先看看这个捋清思路,这里就不叙述了。

CPU 阶段

中断的 CPU 阶段,也可以说是软件操作系统工作的部分,大致分为两个部分,定为中断处理程序和运行中断处理程序两个部分。前文有说过,这里再简要地再说一下:

  1. 根据 vector 去 IDT 中索引相应地中断描述符
  2. 判断特权级是否发生变化,如果中断发生在用户态需要切换到内核态
  3. 若发生特权级变化,需要保存用户态地 SS,ESP 到内核栈,否则不需要保存,然后再保存 eflags,cs,ip 到内核栈中,如果有错误码,还要将错误码压进栈中
  4. 根据中断描述符中的段选择子再去 GDT 中索引相应的段描述符得到段基址,与中断描述符里的段偏移量结合起来找到中断处理的入口
  5. CPU 执行中断处理程序
  6. 执行完后 iret 返回,就是出栈和恢复上下文的一些列过程。

这是以前发过的定为中断服务程序的一张图:

图片说明

关于 CPU 阶段的配置,最为主要的就是配置好 IDT 表,IDT 可以看作是一个个按照中断向量号大小排好序的中断描述符组成的一张表,所以配置初始化 IDT,主要就是设置好一个个中断描述符。而中断描述符由相应的中断处理程序所在的段选择子,段偏移量和相关属性组成,设置中断描述符就是将这些信息按照中断描述符的格式组合起来。配置好 IDT 表之后还要将 IDT 的地址界限加载到 IDTR 寄存器里面,这样中断的时候 CPU 才知道要去哪儿寻找 IDT。

总结

如果不讨论 APIC 和操作系统部分的细节,使用中断控制器 APIC 的中断过程应该还是很清楚的,从外设的中断信号到 IOAPIC 封装成有格式的中断消息,发送给相应 LAPIC,LAPIC 再由一系列决策机制发给 CPU 来处理。至于 CPU 的处理又大致分为两个过程,定位和运行中断处理程序。

但是要论其中的细节的话,整个 APIC 的机制实在是太复杂了,这方面的中文资料很少,有个大牛写了篇 interrupt in linux,对中断机制感兴趣的话强烈推荐看一下。我看了几遍,深有体会,大致理清了中断的过程,APIC 如何工作,但其中的某些地方还是很模糊,还是得去看 intel 的开发手册去了解原委。原名叫 Intel® 64 and IA-32 Architectures Software Developer’s Manual,卷3 第十章详细的讲述了 APIC,很多很详细,英文的,我这个英语渣,啃起来起来实在费力。这几天下来 APIC 的知识没学到好多,英语能力倒是感觉又变强了。而且在一些领域,英文的资料的却也是要详细些准确些。

另外本身也不是从事这方面工作研究的,钻研过深也无多大意义,了解到这个程度自认为可以了,有用到时比如需要系统编程,比如操作系统最初对中断 APIC 的初始化配置,到时能翻手册查阅就行。

但又回到那个点上,各个详细资料大多都是英文的,所以本文总结下来:一定要学好英语!!!

本文就到这里了,有什么错误还请批评指正,也欢迎大家来同我交流讨论共同进步,公号:Rand_cs。

全部评论
请问interrupt in linux有传送门吗,或者能指出出自哪吗,感激不尽
点赞 回复 分享
发布于 2021-12-22 10:32

相关推荐

2025-12-27 16:21
已编辑
门头沟学院 Java
bg:中下211本科,java后端,无竞赛,无基础,大一升大二暑假开始学java。五段实习:美团-小红书-腾讯-淘天-字节。面秋招的简历只有美团、小红书、淘天。刚刚发现我的秋招蚂蚁流程挂了,这是我最后一个流程,那么我的秋招就算彻底结束了,总结一下:字节ssp+,职级2-1。美团ssp,+2打了半小时微信电话极力挽留。快手ssp,但报了字节薪资后没有争取的想法了。小红书sp,今年小红书给的很高,但比字节2-1还是差很多。虾皮应该是小sp?对虾皮一点意向都没,纯拿来集邮了。淘天ssp(暑期转正),说不要我的三方,毕业前考虑好了随时可以不签三方选择淘天。挂了的流程:京东二面挂,估计学历被卡了。懂车帝一面挂,和面试官聊不来,不认同我的方案。拼多多hr面挂,问我低于预期还来不来,当时说不考虑了,估计觉得我不忠诚。蚂蚁hr面挂,聊的还行,但估计我不会去给我挂了吧。阿里控股一面挂,没面前就知道是kpi了,因为时间可选的很多,而且都是半小时,我也拿他刷我的kpi了。上面差不多是我的情况,下面是我想说的话。我觉得我不算特别突出优秀的那类人,但我多少也算是靠前的那一批人,即使这样,秋招也不算特别顺利,也有挂了的流程,但你能说是我的问题吗,我觉得大部分情况不是的,如果真的是我的问题,我不可能本科校招拿到2-1,所以很多面试挂了,问题不出在面试者身上,很多是看运气+眼缘+和面试官合不合得来。所以我觉得,学会察言观色,了解面试官的脾性,也是面试很重要的一个点。比如面试官是喜欢听长回答,还是听短回答,他更看重哪些点,每个面试官对这些的侧重都是不一样的,所以作为面试者,要学会察言观色,通过面试官开局的一两个问题以及你回答后他的表现,就要判断出来。像我现在其实面试开局个五分钟,我就基本能判断个七七八八了,然后我后面的回答就会有所变化。这是我想说的第一个点:不要为面试结果焦虑,有时候问题不出在你身上,但你可以学一些面试技巧,尽量提高你的面试通过率,这里说的面试技巧指的不是网上那种烂大街的,一两分钟短视频说什么提高你面试通过率的,而是你要在你自己的面试过程中不断总结经验,吸取教训,旁人教你的终究是有限的。另外想说下选offer的事,上面其实可以看出来,我秋招最后是选了字节的,还没签三方我就来提前实习感受业务了,当我签完三方又过了一个多月,我这些天又在想这个问题,字节真的是我想要的吗,我现在总结了一下字节的好坏,发现当时可能被字节的高薪资影响判断了,如果现在再选一次的话,我应该会选杭州的小红书,会生活的更舒服点。具体种种就不展开说了。然后虽然我现在也可以说去把小红书舔回来,去毁字节,但我觉得没必要这么做,我可以采用其他的措施去不就,比如规划好两年内就跳槽,跳到杭州,跳到更舒适的城市。我觉得大家选offer的时候,真的可以冷静下来多方面考虑,薪资、城市、组内氛围、业务、老板是否看重、组内情况、未来升职机会等等都是可以考虑的因素,虽然有的时候不管选哪个,都不会坏,但最好也别让自己后悔吧,即使真后悔了,我觉得也没必要过度美化没走过的路,想好补救措施即可。这是我想说的第二个点:冷静好好做选择,不管是offer还是其他。但人生容错率很大,即使选错了,也一定有补救措施。最后还想说一些成长上的东西,尤其是现在AI火热的时代。我觉得大家如果想提高自己,或者说在未来社招跳槽有竞争力,肯定是要学AI相关的东西的,不说要会多懂AI,至少也要了解基本概念,而且一定要学会用AI提效。我现在字节的mt和我说,他现在80%代码都是AI写的。而我最近也开始尝试用AI工具,感觉现在AI真的进步很多,挺聪明的了,我现在写需求基本都是先让AI写,我再人工review小改动一下就差不多了。我觉得「AI取代程序员」是个很远的话题,但是「AI取代不会用AI的程序员」,可能真的就是近两年的事了。而怎么去学习这块的内容,其实我也正在探索,我也是刚学AI的起步阶段,我觉得大家也要有自己的信息检索能力,而不是别人喂你什么,你才学什么,自己一个人就不会学了。这是我想说的第三个点:趁年轻,多学习提升自己,拥抱AI,不要原地踏步,原地踏步的程序员最容易被淘汰。大概就是这样吧,今天看蚂蚁流程发现挂了,前几天腾讯约面我也拒了,就想到自己的秋招/校招算彻底结束了,有感而发,随便聊了下。牛客以后应该不会更新,大家不用关注,熟悉我的朋友应该知道我在其他平台有号。我更喜欢以长视频的形式去做分享,感觉会更有体系,而不是网上那种一两分钟的零碎短视频的那种营销号去起号,我也推荐大家多去看高质量的长文章、长视频,我觉得收获的能更多。希望大家能收获满意的offer与未来。
CEXBB:刷到最后才发现原来是优雅✌🏻,我的Java引路人
2025年终总结
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务