当前位置: 首页 > 科技观察

Linux中断子系统:中断处理知识点

时间:2023-03-12 08:16:11 科技观察

Linux中断相关节点/proc/interruptscat该节点会打印系统中所有的中断信息。如果是多核CPU,每个核都会打印出来。包括每个中断的名称,中断号IRQ号,每个中断被触发的次数,处理的是哪个CPU核,是边沿触发还是电平触发,属于哪个中断控制器,都会打印出来。/proc/irq/...进入这个目录。您将看到以中断编号命名的文件夹。每个中断号文件夹下有几个节点,里面存放着中断的信息,比如smp_affinity、affinity_hint、spurious等。smp_affinity表示中断号和核心CPU之间的亲属绑定关系,即如果绑定了一个中断号到一个CPU核心,那么中断将始终在这个CPU上处理。如何让一个中断由特定的CPU处理?Kernel2.4及之后的版本支持将不同的硬件中断请求(IRQ)分配给特定的CPU。这种绑定技术称为SMPIRQAffinity。更多介绍请参考Linux内核源码自带的文档:linux-4.14/Documentation/IRQ-affinity.txt/proc/irq/{IRQ}/smp_affinity/proc/irq/{IRQ}/smp_affinity_list/proc/irq/{IRQ}/smp_affinity指定允许给定irq中断源执行哪些CPU。是一个掩码位,比如ff,代表11111111,意思是可以在8个CPU上执行,执行哪个CPU取决于分发器。分发。如果这个/proc/irq/{IRQ}/smp_affinity指定为00000001,则表示这个IRQ只能由最后一个CPU核处理,不允许其他CPU处理。你可以测试一下。博主测试是可以的(GIC支持,其他中断控制器不一定)。手动分配的串口重启后会消失。可以在代码中调用irq_set_affinity函数指定中断掩码,以满足中断由固定CPU处理的要求。中断分配机制对于GIC-V2,SPI的分配是根据Distributor中的InterruptProcessorTargetsRegisters决定的。对于任何SPI,GICD_ITARGETSRn寄存器中有8位用于标识交付的处理器。如果只设置一位,那就很简单了。如果该中断是当前优先级最高的中断,那么Distributor就会将其发送到对应的CPU接口,最终将中断发送到指定的CPU。如果中断对应的InterruptProcessorTargetsRegisters中的8位中有多个位被置位,Distributor如何处理呢?“将产生的中断依次发送给每个CPU,或者给哪个CPU空闲,CPU来了”,这样复杂的逻辑由硬件来处理是不合适的。实际上GIC的硬件是不会做任何判断的,也不会集成任何算法。它基于InterruptProcessorTargetsRegisters的位设置,忠实地将中断发送到指定的处理器或多个处理器。你可以看一下gic_set_affinity函数。此函数确保仅设置中断的中断处理器目标寄存器中的8位之一。/kernel5.15/drivers/irqchip/irq-gic-v3.c在1244和1246行,1246行是选择在线CPU之一,1263行写入寄存器,GIC会读取这个寄存器,这一个是吗?CPU,然后向这个CPU发送中断。中间的函数很简单,大家自己看吧。中断状态机对于GIC-V2,中断状态机由Distributor维护,每个中断都有一个状态机。Inactive:中断未激活(未发生)。Pending:中断到达GIC,等待CPU处理。Active:中断被CPU确认,中断被CPU处理。Activeandpending:CPU正在处理一个中断,此时中断又来了。我们来看一个例子:(a)N和M用来标识两个外设中断,N的优先级高于M(b)两个中断都是SPI类型,电平触发,高电平有效(c)两个中断都配置到同一个CPU(d),配置为group0。Distributor通过FIQ检测T0时刻中断源M的有效触发电平触发中断时间事件T0。Distributor将中断源M的状态设置为pending,在T17大约15个时钟后,CPU接口拉低nFIQCPU信号线,向CPU上报M外设的中断请求。此时CPU接口的ack寄存器(GICC_IAR)的内容会被修改为M中断源对应的ID。在T42,Distributor检测到一个优先级更高的中断源N的触发事件。在T43,Distributor将N中断源的状态设置为Setaspending。同时,由于N有更高的优先级,Distributor会标记当前优先级最高的中断。在T58大约15个时钟后,CPU接口拉低nFIQCPU信号线,向CPU上报N外设的中断请求。当然,由于CPU已在T17断言,实际电平信号仍保持断言。此时CPU接口的ack寄存器(GICC_IAR)的内容会被更新为N个中断源的ID。在T61时,软件读取ack寄存器的内容,得到当前优先级最高且状态为pending的中断ID(也是N个中断源对应的ID),通过读取该寄存器,CPU也ack了中断源N。此时Distributor将中断源N的状态设置为pending和active(因为是leveltrigger,只要外部还有assertedlevel信号,就一定是pending,中断是beingprocessingbytheCPU.,所以状态是pending和active)注:T61表示CPU开始服务中断T64.3个时钟后,由于CPU已经ack到中断,GIC中的CPU接口模块deassertsnFIQCPU信号线取消发送给CPU的中断请求在T126,因为中断服务程序操作了N外设的控制寄存器(ack外设的中断),N外设取消断言其中断请求信号。在T128,Distributor释放了N个外设的pending状态,所以N的中断源状态置为activeT131时,软件操作EndofInterrupt寄存器(将N对应的中断ID写入GICC_EOIR寄存器),标志着中断处理的结束。Distributor改变中断源N的状态为idle。注:T61~T131为CPU服务N个外设中断的时区。在此期间,如果有高优先级的中断挂起,就会发生中断抢占(硬件的意思),此时CPU接口会向CPU断言一个新的中断。在T146时刻大约15个时钟后,Distributor向CPU接口报告当前挂起且优先级最高的中断源,即M。经过漫长的挂起,M终于迎来了春天。CPU接口拉低nFIQCPU信号线,向CPU上报M外设的中断请求。此时CPU接口的ack寄存器(GICC_IAR)的内容会被修改为M中断源对应的ID。在T211,CPU确认M中断(通过读取GICC_IAR寄存器),并开始处理低优先级中断。Linux抢占机制GIC中断控制器支持中断优先级抢占,一个高优先级的中断可以抢占一个低优先级的活跃中断,即GIC仲裁单元会记录并比较当前最高优先级的挂起状态,然后抢占当前中断,并向CPU发送最高优先级的中断请求。从GIC的角度来看,GIC会向CPU发送高优先级的中断请求。但是CPU不一定响应!!!因为在中断处理过程中,CPU处于中断关闭状态(关闭CPU),需要等待低优先级中断处理完毕,直到EOI发送给GIC,然后CPU才会响应处于挂起状态的优先级最高的中断被处理。所以在Linux下:1.高优先级中断不能抢占正在执行的低优先级中断。2.同样处于pending状态的中断会优先处理,以响应高优先级的中断。3.对于优先级和pending状态相同的中断,选择硬件中断号ID最小的中断发送给CPU。这是可以理解的。如果大量中断爆发,如果允许中断嵌套,栈会越来越大,会爆炸。所以,为了防止这种情况发生,Linux中的中断是不允许嵌套的。在单个CPU中,直到处理完一个中断,才会响应另一个中断,即使优先级比它高。在FreeRTOS中,允许高优先级的中断抢占正在执行的低优先级中断,不同的系统设置不同。中断和进程调度是一个复杂的机制。根据不同的需求,调度机制会在不同的时间切换。CPU会根据进程的优先级、时间片等信息调度不同的进程。中断可以打断一个进程的运行,任何一个中断的优先级都高于所有进程。在中断处理过程中,主要是GIC与CPU的交互。即使GIC支持高优先级中断抢占正在执行的低优先级中断并向CPU内核发送信号,CPU内核也不需要处理它们,因为在Linux中,当CPU内核执行到中断处理时,它处于关闭中断和关闭抢占的状态,不再响应中断信号。也就是说,在中断优先级的概念中,只有当GIC中同时有多个挂起的中断时,此时才会选择最高优先级的中断执行,高优先级的会抢占低优先级的中断(即使如果低优先级在前)。来)。如果低优先级中断处于激活状态,则不能被抢占。这是上下文。只有当状态同时处于挂起状态时才会存在抢占。为什么Linux不允许中断休眠?所谓sleep就是调用schedule让出CPU,由scheduler选择另外一个进程继续执行。这个过程涉及到进程栈空间的切换。1、如果schedule是在中断上下文调用的,此时获取的structthreadinfo数据结构是中断发生时的进程栈信息,不是中断上下文调用schedule时的任何信息。这使得无法返回到中断上下文中调用schedule的地方。2.中断上下文正在关闭中断。需要发送一个EOI通知GIC中断处理结束,然后GIC和CPU接口会进入下一次中断处理。如果中途调度,整个系统的中断都会被屏蔽。一般进入中断后,需要关闭中断,还要关闭抢占。同时,注意不要打电话给日程表。unhandledinterruptandspuriousinterruptunhandledinterruptandspuriousinterrupt在中断处理的最后,总会有一段代码如下:desc,retval);returnretval;}note_interrupt用于未处理中断和虚假中断处理。对于这种类型的中断,linux内核有一套复杂的机制来处理,可以通过命令行参数(noirqdebug)来切换这个功能。当中断发生但未被处理时(有两种可能,一种是根本没有注册具体的handler,第二种是有handler,但是handler否认是其对应设备触发的中断),我应该怎么办?毫无疑问,这是一种异常情况,那么内核是否应该立即采取措施禁用IRQ呢?不太合适,毕竟允许共享中断请求信号线,直接禁止IRQ可能太苛刻了,内核采取了这样的策略:如果IRQ被触发10万次,但是99900次没有处理,在这种情况下,我们禁用中断请求线。中断线和中断号是一个意思。相关的控制数据在中断描述符中,如下:structirq_desc{...unsignedintirq_count;--------记录发生的中断次数,每100,000次回滚unsignedlonglast_unhandled;-----最后一个unhandledIRQ的时间点unsignedintirqs_unhandled;------没有处理的次数...}中断生命周期