十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

CPU特权资源与中断机制:从内核态切换到嵌入式实战的完整链路解析

CPU特权资源与中断机制:从内核态切换到嵌入式实战的完整链路解析 我先说个事情。最近在啃CPU底层相关的知识发现一个很有意思的现象很多做嵌入式写了三五年中断服务函数的人你问他“中断进来以后CPU到底是怎么切到内核态的”他大概率会愣一下然后告诉你“反正配置好优先级就能跑”。这其实不怪他因为中断和特权资源这两块知识恰好卡在“软件够得着、硬件看得见”的中间地带教程里讲得太散要么往操作系统方向飘要么往数字电路方向沉很难有一篇文章把这两件事之间的关系讲透。所以这篇笔记我就围绕CPU里的特权资源和中断机制来写。先说清楚特权资源是什么、为什么CPU非要搞出“内核态/用户态”这种等级制度再拆解中断从触发到返回的完整链路以及中断和特权机制是怎么配合的。最后落到实操层面聊几个我在STM32上写中断程序时踩过的坑和排查思路。无论你是学操作系统、做嵌入式还是对计算机组成原理感兴趣这篇内容应该能帮你把这两块知识真正焊在一起。1. 为什么CPU要用“特权资源”来武装自己1.1 一个系统里总得有人说了算用户态与内核态的由来想象一下如果一台手机上的所有App都能直接改CPU的寄存器、关闭中断、修改页表那系统会变成什么样一个普通的音乐播放器误操作把内存保护关了另一个App就能顺手读到你的银行验证码。整个系统就会变成 “谁都能指挥CPU”的混乱集市。所以CPU设计者从一开始就意识到不是所有指令都该让所有程序随便执行。于是就有了“特权级”的概念。最典型的就是x86的ring 0~ring 3以及ARM的EL0~EL3还有MIPS的kernel mode和user mode。名字不一样核心思想完全一致—— 把CPU的运行状态划分成好几个等级级别高特权级深的代码能访问所有资源级别低特权级浅的代码只能老老实实待在自己的沙盒里。这里要理解一个关键点CPU硬件本身并不懂什么是“操作系统”它只知道当前处于什么模式。模式决定了当前执行的指令能不能碰特权资源。一旦用户态程序试图执行特权指令CPU会当场翻脸——触发一个异常exception跳转到固定的处理入口然后由更高级别的代码决定怎么收拾这个不守规矩的进程。整个过程是硬件强制执行的不是操作系统想拦就拦而是CPU用物理逻辑在拦。1.2 特权资源到底包括哪些管的是什么事特权资源不是某个具体的硬件模块而是一类“只有特权模式才能操作的资源”。我按用途给你梳理一下。第一类是控制类寄存器。比如x86的CR0、CR3MIPS的CP0寄存器组ARM的SCTLR。这些寄存器控制着分页开关、缓存开关、异常入口基地址等核心参数。你要是让普通程序能改CR0关掉分页那所有虚拟地址立刻变成物理地址整个内存保护体系就形同虚设了。第二类是中断和异常相关的配置。包括中断屏蔽位、中断使能寄存器、中断向量表基址如VBAR。为什么中断配置算特权资源因为中断是CPU响应外部事件的核心通道谁能控制中断谁就能控制CPU的注意力分配。如果一个普通进程能任意关中断那它系统调用的时候把中断一关整个系统就瘫痪了。第三类是内存管理相关配置。比如页表基地址寄存器以及涉及访问内存映射I/O的地址区域。这部分比较好理解页表决定了虚拟地址到物理地址的映射关系也决定了哪些内存页可读、可写、可执行。这个东西如果没有特权保护普通程序给自己映射一块可执行的物理内存再写入Shellcode那代码执行层面的防线就彻底崩了。第四类是想一想就能明白的I/O端口访问指令部分架构、停机指令、读/写特殊功能寄存器等。这些特权指令共同的特征是执行后对CPU状态有全局性影响或者能绕过已有的内存/访问保护机制。1.3 模式切换每一次“下凡”和“升天”都不是免费的既然分成了特权级那程序在不同特权级之间跑肯定需要一套“换挡”机制。这套机制就是异常和中断。——你看中断在这里就开始露头了。用户态程序想访问磁盘、读网络数据、申请内存怎么办它不能直接调内核函数因为那些函数运行在特权模式函数地址也对用户不透明。于是操作系统提供了一组“系统调用”接口。用户程序执行一条特殊指令比如x86的syscall/sysenterARM的svcMIPS的syscallCPU会立刻切换到内核态跳到内核预设的入口地址然后内核根据系统调用号去执行对应的服务。执行完再用一条特殊返回指令sysret/eret切回用户态。这里要特别注意模式切换意味着CPU要保存现场、加载内核栈、跳转入口这些操作都会产生时间开销。为什么程序员常说“系统调用慢”不全是内核逻辑慢光是那条边界切换指令本身就需要做很多事。因此也衍生出了尽量减少系统调用次数、用批处理方式合并请求等性能优化思路。对于做嵌入式开发的朋友这一点在RTOS里感受更明显每次任务切换或者触发异常CPU都要花不少时钟周期去压栈、弹栈。特权资源的本质其实就是“CPU的信任模型”。谁掌握特权资源谁就掌握系统的命运。所以所有现代CPU都会用硬件的强制规则来保护它。2. 中断让CPU学会“随时停下手里的事”2.1 中断到底是个什么东西一次“电话铃响”的全程拆解聊完了特权资源终于轮到重头戏——中断。你可以把CPU想象成一个正在吭哧吭哧写代码的程序员。正常情况下他得一行一行往下执行但这时候电话响了。他有两种选择一是无视电话继续写代码这种叫轮询二是先停下手里的活接电话、处理完、再回到刚才写代码的位置继续写这种就叫中断。放在CPU的世界里中断的本质就是外部事件或内部事件请求CPU停止当前执行流转而去执行一段专门的处理程序中断服务函数ISR处理完之后再回到被中断的指令处继续往下执行。为什么需要中断因为CPU的速度和外设的速度差了太多。拿串口举例串口波特率115200一秒最多也就一万来个字节而CPU一秒钟能跑上亿条指令。如果你用轮询方式等串口数据那CPU就得不停地去查看接收寄存器有没有新数据大部分时间都在空转。中断机制让CPU可以去做其他事情等串口收到一个字节后再被“叫”去处理处理完再回去效率完全不在一个量级。2.2 中断分类硬件中断、异常与软中断的区别很多人把“中断”和“异常”混为一谈其实在CPU体系里它们是一对表亲但可以说是性格迥异的表亲。硬件中断也叫外部中断是异步的。它来自CPU外部比如串口收到数据、定时器计数溢出、GPIO引脚电平变化。CPU执行到哪条指令跟它没关系随时可能发生。这也是阅读代码流程时最让人头疼的一种。异常也叫同步异常是CPU内部在执行指令过程中产生的。比如除法指令除以零、访问了不存在的内存地址缺页、执行了特权指令但当前模式不允许。它们有个特点和当前指令严格同步同一段代码每次执行到这条指令都会触发同一个异常。软中断在ARM里叫SVC在x86里叫int指令本质是一种主动触发的“异常”。用户态程序想进内核态做事情不能直接跳因为硬件不允许跨特权级直接跳转必须通过这个专用的“陷阱指令”来触发模式切换。所以系统调用的底层实现往往就是一条软中断/陷阱指令。2.3 中断向量表与响应流程从引脚电平到执行ISR只差五步CPU如何处理一次中断我把标准的流程拆成五个步骤这比你看某个厂商的芯片手册要通用得多。第一步是“采样识别”。CPU内部有专门的中断控制器比如STM32的NVIC、x86的APIC、RISC-V的PLIC它会不断地采样各个中断源的电平或边沿信号。如果检测到有效的中断请求并且该中断没有被屏蔽、优先级足够高就会向CPU核心提交一个中断请求信号。第二步是“响应准入”。CPU在执行完当前指令后发现中断请求会判断当前是否允许响应。如果允许就开始走中断响应流程。这里有个细节CPU不会在指令执行到一半的时候被中断打断至少会等当前指令执行完有些复杂指令比如字符串搬运指令还允许中途被中断但CPU要能恢复到指令中间的状态设计很复杂。第三步是“保存现场”。CPU会把“当前执行到哪了”这件事保存下来。在ARM里是把返回地址和CPSR保存到对应模式的保存寄存器在x86里是把CS、EIP、EFLAGS压入栈。不同架构做法不同但目的都一样——保证中断返回后程序能无缝衔接。第四步是“查表跳转”。CPU根据中断号去查中断向量表。这张表里存着每个中断对应的处理函数入口地址。拿到地址后CPU跳到这个地址开始执行中断服务函数。第五步是“恢复返回”。ISR执行完毕执行中断返回指令比如ARM的eret、x86的iretCPU把之前保存的现场恢复回来继续执行被中断的指令。这五步看起来简单但每一步都藏着架构设计者的深意。尤其是“现场保护”这一步牵涉到栈切换、寄存器组切换、状态保存等也是中断性能的关键所在。2.4 中断优先级、嵌套与屏蔽别让抢车位毁了你的系统如果同时来了好几个中断CPU该听谁的这就涉及中断优先级和嵌套了。优先级这个概念不用多解释高优先级中断能抢占低优先级中断的执行。但这里有个容易忽视的点中断是可以嵌套的。比如正在处理串口接收中断这时定时器中断来了如果定时器优先级更高CPU就会暂停串口中断服务先处理定时器中断处理完再回到串口中断。这个行为和函数调用有点像但有一个非常重要的区别函数调用是你主动调用的嵌套深度可控中断是随机发生的嵌套深度不可预测而栈空间是有限的。因此中断服务函数必须短小精悍不能在里面干太多事情否则嵌套一层又一层总有一天栈会爆。屏蔽Mask是另一种控制手段。你可以在CPU级别屏蔽所有可屏蔽中断也可以针对某个具体中断源单独屏蔽。进入临界区的时候通常要临时关中断保证这段代码不会被中途打断。但关中断时间绝对不能太长否则外部事件无法得到响应系统实时性就差了。优先级配置是一个需要特别小心的工程问题。在STM32上你可能会设置一个低优先级中断和一个高优先级中断高优先级的中断里有一个延时循环此时如果低优先级事件触发它会一直等待但它的中断标志位如果没被及时清除低优先级中断反复请求CPU就会反复被“拦下来问一句能不能处理”导致系统响应变慢。这种问题在调试时非常隐蔽稍后我会专门讲。3. 特权与中断的“梦幻联动”为什么中断是进内核最快的门3.1 中断入口默认切内核态这是硬件写死的行为现在关键问题来了中断和特权资源到底什么关系回归本质——当硬件中断来临时CPU会被动地切换模式通常是切换到特权模式。以ARM Cortex-A系列为例普通用户进程运行在EL0当外部中断IRQ触发时CPU会陷入EL1内核态跳转到EL1的异常向量表入口由内核的中断处理流程来统一处理。这个“陷入”动作由硬件自动完成用户态程序没有任何机会插手。这是整个操作系统安全模型的重要支柱之一中断处理必定发生在特权模式下。试想如果中断发生在用户态而不切换模式那么中断服务函数就能拿到用户态的权限去访问特权资源——你已经能想象到这会打开多大的安全口子。恶意程序完全可以伪造一个中断请求然后在一个“拥有特权模式”的上下文里执行任意代码整个系统就沦陷了。所以CPU设计者们在硬件层面直接规定异常和中断入口天然属于特权模式。这也是“中断是进内核最快的门”这句话的含义。你不需要构造任何复杂调用一条硬件中断线就能把你从用户态拖进内核态。3.2 现场保护、栈切换与返回软硬件各干一半的默契既然要在特权模式下处理中断那CPU得先回答一个问题处理中断时用哪个栈用户进程的栈是虚拟内存里的一个区域内核如果直接用它既危险也不方便。因此每个进程在内核空间里都有一个独立的内核栈中断触发后CPU会从用户栈切换到内核栈。这个切换动作在ARM和x86上实现方式不同。x86在特权级变化时会从TSS任务状态段里找到内核栈的地址自动完成栈切换同时把用户态的SS、ESP、EFLAGS、CS、EIP压入内核栈。ARM则是把返回地址和CPSR保存到对应异常模式的私有寄存器中然后模式自己切换到内核态由异常处理代码来统一压栈。这种“软硬件各干一半”的分工在中断返回时同样重要iret或eret指令会一次性恢复CPU状态同时完成模式切换和栈切换。这里我要补充一个容易踩坑的细节裸机环境和RTOS环境对中断栈的管理方式不一样。裸机里你可以就着一个固定数组做栈中断嵌套深度靠估算但RTOS里每个任务都有自己的栈空间中断发生时保存的临时状态占用的其实是“当前任务”的栈。如果任务栈尺寸设置不够中断嵌套一多就会溢出表现出的现象常常是某个任务跑着跑着突然“神秘崩溃”查半天查不出原因。3.3 系统调用的本质一场“用户态主动要求下凡”的特权交接我说过系统调用是通过软中断指令实现的。这条指令的特殊性在于它本身就是一种“异常”由CPU硬件强制执行特权级切换。所以你可以把用户态发出系统调用这个过程理解为一次“用户态主动请求进入内核处理事务”的中断式交接。有读者可能会问既然中断和系统调用都会切内核态那它们在内核里是同一个处理路径吗早期内核会区分“来自中断的上下文”和“来自系统调用的上下文”因为中断可能打断任意进程而系统调用必然发生在某进程的主动请求里。内核能拿到进程上下文的信息来做事比如睡眠、调度等。但在中断上下文里内核通常不允许睡眠也不能随意切换任务否则现场会乱掉。这个区别是理解“上半部/下半部”机制的关键。如果后面有机会围绕Linux内核里中断下半部的机制、tasklet、工作队列来写一篇这里就不展开了。4. 实战现场从STM32看嵌入式场景的中断编程4.1 串口接收中断最基础也最容易翻车的入口说了这么多理论终于到实际写代码环节。我拿STM32来举例因为它的NVIC中断控制器算是同类里比较好理解的一个而且网上例程多方便你对照验证。最基础的串口接收中断初始化流程大概是配置UART引脚、设置波特率、使能UART接收中断__HAL_UART_ENABLE_IT(huart1, UART_IT_RXNE)、然后调用HAL_UART_Receive_IT启动接收。中断触发后在UART_IRQHandler里会调用HAL_UART_IRQHandler。这里我要说一个新手最容易踩的坑很多人直接在串口中断服务函数里做字符串处理比如拼接、解析、甚至发送应答结果系统一跑起来就卡死或者丢数据。根因很简单串口中断是一个字节一个字节触发的如果没开IDLE中断在115200波特率下大约86微秒就来一次中断CPU如果还在处理上一帧数据新字节到来时接收寄存器就被覆盖了数据就丢了。正确做法是中断服务函数里只负责把数据存进环形缓冲区置一个标志位真正解析处理的逻辑放到主循环里做。这种模式叫“中断下半部”在实际应用中的简化版。另外很多人在用HAL库时搞不清“中断标志位什么时候清”。HAL库在读取完数据寄存器后会自动清除RXNE标志但如果你直接操作寄存器就一定要记得主动清除。这个标志不清中断会一直触发代码就卡死在中断入口附近表面现象是主循环“死”了实际是中断风暴把CPU资源全部吃光了。4.2 定时器中断与按键中断你以为简单坑都在细节里定时器中断是另一个高频场景。我早期写单片机代码时喜欢在定时器中断里做软件延时、做计时累加、甚至做LED闪烁后来发现一个问题所有的时间基准都挤在同一个定时器中断里一旦加入新的业务逻辑中断时间变长其他依赖该定时器的任务全部跟着抖动。定时器中断的通用原则是中断里只做“时间标记”把flag 1或者把当前计数值存下来返回主循环后再根据时间差去做延时控制。如果你想做精确的微秒级延时可以用定时器硬件输出比较或者捕获比较功能不要用软件循环因为CPU的时钟频率一旦变化延时就不准了。按键中断的情况更有意思。按键接到外部中断引脚按下触发下降沿看似简单但按键天生带机械抖动——按下瞬间电平会快速跳变多次每次都触发中断于是本来只想处理一次按键实际触发三五次。解决方式有两种硬件上去抖电容软件上在中断里记录时间只有在距离上一次有效触发超过一定时间比如20ms之后才认为是一次新按键。我建议做产品时硬件去抖电路和软件去抖逻辑一起上双保险。4.3 中断服务函数编写的六条军规结合上面几个实战场景我把中断服务函数的编写经验总结成六条简单说就是“16字干话加两条补充”第一函数体要短。中断服务函数不要超过几十行最长执行时间控制在微秒量级。内部只做必须立即处理的事清标志、读数据、置标志位。真正复杂的逻辑放到主循环里做。第二不要阻塞。不要使用delay、while等待标志、甚至printf这类耗时操作。printf底层会去轮询发送寄存器串口慢的时候一次要几百微秒对中断系统是灾难。第三不要调用不可重入函数。像malloc、sprintf这类标准库函数内部可能有隐藏状态不是线程安全的在中断里调用容易导致数据竞争。第四注意volatile。中断和主循环共享的变量必须用volatile修饰防止编译器优化成读取寄存器副本。你可能碰到过这种怪事主循环里sprintf一个共享变量值怎么都不跟中断里更新的一致原因就是编译器把变量优化进寄存器了每次读到的还是旧值。第五遵循“标志位缓冲区”的通信模式。中断产生数据主循环消费数据。缓冲区我优先推荐环形缓冲区不用频繁关中断多字节数据也自然衔接。第六中断标志位一定要清除。每个中断源都有对应的标志位处理完必须清除。漏掉一个就是中断风暴清错一个就是中断丢失。4.4 中断优先级分组配置错一步整个系统都乱STM32的NVIC支持中断优先级分组这个概念理解错了会非常头疼。优先级分组决定了“抢占优先级”和“子优先级”各占多少位。抢占优先级高的中断可以打断正在处理的低优先级中断子优先级只在同抢占优先级的中断同时发生时决定先后顺序。举个例子如果你把分组配置为NVIC_PRIORITYGROUP_4那么4位全部是抢占优先级没有子优先级所有中断都能互相打断。这个配置适合那些对实时性要求高、中断本身很短的小系统。如果配置为NVIC_PRIORITYGROUP_0那4位全是子优先级所有中断同抢占优先级谁都不能打断谁只能排队。这种方式适合中断链比较短、不希望嵌套过多的情况。一个常见问题某个外设中断一直不能打断正在执行的中断你查了赋值也没错。注意检查优先级分组的配置是否和中断初始化时一致。NVIC分组是一个全局配置一旦设置对所有中断生效。如果你在底层初始化时把分组设为Group 2在应用层又按Group 4的数值去设置单个中断的优先级数值对应的抢占优先级和子优先级位段就错位了中断的行为就会变得“怪怪的”——很隐蔽。5. 踩坑记录CPU占用率100%与中断异常排查实录5.1 一次“中断风暴”导致的CPU占用率100%之前调试一个设备发现CPU占用率莫名其妙到了100%界面操作卡顿但功能看起来还在跑。我一开始以为是主循环里某个模块死循环了打日志、单步调试都试过问题依旧。后来用示波器抓一个闲置GPIO的翻转频率才发现中断服务程序的执行频率高得离谱。排查步骤是这样的先看主循环是否卡住再统计中断触发频率。我临时写了一个计数器每进一次定时器中断就加一然后通过串口上报。结果发现计数器的增长速度是理论值的几十倍。顺着中断源查下去发现是DMA接收中断的标志位没有被正确清除——HAL_DMA_IRQHandler在接收完成中断后需要调用__HAL_DMA_CLEAR_FLAG清标志但我在重写中断回调时无意中把默认清除逻辑覆盖了。于是DMA传输完成后标志位一直置位中断被反复触发CPU大部分时间都在“响应”一个已经处理完的中断请求自然占用率拉满。这种问题排查思路就是“看中断频率、检标志位”。如果某个中断源的触发频率异常高于正常值大概率就是标志位没清干净导致的中断风暴。5.2 中断丢失我遇到的三次“信号没了”事故中断风暴是“太多了”中断丢失则是“不见了”。我第一次遇到中断丢失是在两个串口同时收发数据的时候。A串口接收数据过程中B串口来了一个高优先级中断抢占了A中断的处理。此时A串口正好又收到一个字节但它的接收寄存器仍被上一次的数据占着没有读走这个新字节就把数据挤掉了。解决方法是降低B的优先级或者A中断里及时读取DR寄存器但更高明的做法是用DMAIDLE方式接收不定长数据批量搬运从根上避免一字节一中断的脆弱性。第二次遇到中断丢失是外部按键中断触发的沿检测问题。GPIO外部中断默认是上升沿或下降沿触发当按键按下和释放的时间非常短半按抖动时会同时检测到两个边沿但中断控制器可能只上报其中一个另一个信号就被当成毛刺滤掉了。解决方式是设置触发方式为低电平触发或者用“边沿定时器扫描”的方式做消抖和检测。第三次更玄高优先级中断嵌套低优先级中断时低优先级中断嵌套到一半高优先级又重新触发导致低优先级ISR没执行完又被抢占。多层嵌套回来后栈上保存的现场有概率被破坏程序跳转到意外地址。这个问题的本质是嵌套深度过深。解决方法是给某些关键中断设置同级不抢占或者干脆关中断保护最关键的数据结构。5.3 中断延迟过大临界区太长的后果中断延迟就是从触发中断到真正进入ISR的时间。这个值取决于三部分当前指令的执行时间、是否在临界区中、中断控制器的响应时间。临界区如果太长中断延迟就会被拉大实时性就崩了。有次我把一个Flash擦写操作直接放在主循环里而Flash擦写是阻塞式的最长要几十毫秒。在这个区间里CPU屏蔽了一部分中断比如I2C事件中断结果I2C总线上从设备等了很久没响应直接超时复位。排查时我用一个GPIO脉冲标记进临界区的时刻再用另一个GPIO标记实际进入ISR的时刻两路波形一对比问题就一目了然了。所以凡是耗时操作Flash擦写、长串口打印、复杂计算都不要和“屏蔽中断”放在同一个区间里。必要的话把数据准备好后在主循环里分片处理。5.4 中断排查工具与通用套路我自己的调试工具箱其实很简单示波器、逻辑分析仪、调试器的断点还有最原始的GPIO翻转法。先说GPIO翻转法。在ISR入口翻转一个引脚在ISR出口再翻转一次用示波器看这个引脚的波形就能测到中断的触发频率和ISR执行时长。这个方法看起来原始但实测比任何调试器都靠谱因为它不会改变程序执行时序。逻辑分析仪更适合并行抓多个信号。你可以把中断请求线、ISR入口、主循环关键节点一起抓到屏幕上看哪个环节的时序对不上。调试器断点在这种场景下其实不好用因为断点会让程序停下来整个时序全乱套特别是外部通信类的bug一断点就复现不了。通用排查套路按这个顺序走先确认中断是否触发了再确认CPU是否响应了再确认ISR是否执行完了再确认执行结果是否正确。每一层都通过“打点”来验证不要跳着猜。6. 顺带聊聊从软件中断知识走到CPU硬件设计6.1 为什么建议有基础后折腾一台MIPS单周期CPU回到文章最开头说的“CPU学习笔记”这个身份。如果你只是写软件可能永远体会不到中断的硬件层面到底是怎么回事。但如果你有精力我强烈建议去玩玩MIPS单周期CPU设计这类项目哪怕只是在Logisim里仿真出来。单周期CPU是整个CPU设计里最朴素的一种每条指令在一个时钟周期内完成。你会亲手搭建取指、译码、执行、访存、写回这几个阶段也会亲手设计寄存器堆和ALU。等跑通基础指令以后再加上中断处理机制你就能非常直观地体会到特权级切换和异常向量表这些概念在硬件上是怎么实现的。我在学习过程中就发现写过一次Verilog的中断控制器再看STM32的NVIC很多细节一下子豁然开朗了。比如“中断向量表基地址为什么要按某种对齐方式存放”“为什么ISR不能太长”这些以前靠死记的结论背后其实都是硬件设计在推动。6.2 从TD4到流水线一次逐步逼近真实CPU的旅程如果你觉得MIPS单周期还是太复杂网易云课堂和B站上有个非常出名的“TD4”项目是一个仅用4位数据位宽的迷你CPU几十行Verilog就能搞定。虽然它简单到几乎没有完整的中断机制但你可以从TD4起步理解指令集、控制器、数据通路的基本框架。再往后就是MIPS多周期CPU和五级流水线CPU了。中断在你做流水线CPU时会变得非常微妙因为流水线里同时有多条指令在执行如果第3条指令触发了一个异常而前后已经有几条指令被改变了状态CPU必须保证异常返回后整个流水线状态是对的。这个“精确异常”的问题是真实CPU设计中非常核心的一环也是一般的软件工程师几乎接触不到的领域。但理解了它你再去看Linux内核里ptrace、信号处理要先备份上下文之类的东西会顺利很多。6.3 学习顺序建议与资源方向我给一条我自认为比较顺的学习路线供参考。第一步先把程序员的视角打牢了解中断向量表、中断优先级、中断嵌套、关中断保护临界区这些概念能做好嵌入式编程里的中断管理。第二步学一点操作系统原理进程切换、系统调用、上半部下半部、软中断和工作队列体会中断是如何被操作系统抽象成一套统一的软件机制的。第三步往下沉一层学习数字电路和CPU设计。先看《编码隐匿在计算机软硬件背后的语言》建立硬件直觉再找一本讲CPU设计的书搭配Logisim或者Verilog仿真实践。第四步如果有硬件条件可以找一块FPGA开发板把中断控制器、UART、定时器对自己做的CPU上跑一遍。这个层级学完你再看CPU天梯图、服务器CPU参数这些市场信息时会有一种“看懂了底牌”的感觉。我自己走到第四步的时候感慨特别深很多看似高深的名词比如特权级、异常向量、中断嵌套说到底就是CPU为了“让一堆程序安全地、协调地共享一套资源”而做的硬件约定。回到最开始的问题中断处理和特权机制是软件和硬件交接最深的地方也是操作系统一切行为的基石。这篇笔记本来只想记给自己看的结果越写越长。如果你也是那种喜欢把底层原理弄明白再写代码的人希望这份内容能帮你少走一点弯路。
返回列表