中断处理 - 上半部(硬中断) 由于 APIC中断控制器 有点小复杂,所以本文主要通过 8259A中断控制器 来介绍Linux对中断的处理过程。 注册中断处理入口 在内核中,可以通过 setup_irq() 函数来注册一个中断处理入口。 处理中断请求 当一个中断发生时,中断控制层会发送信号给CPU,CPU收到信号会中断当前的执行,转而执行中断处理过程。 处理完中断后,调用 do_softirq() 函数来对中断下半部进行处理(下面会说)。 中断处理 - 下半部(软中断) 由于中断处理一般在关闭中断的情况下执行,所以中断处理不能太耗时,否则后续发生的中断就不能实时地被处理。
5、 在CPU可以处理下一个中断的时候,从IRR中选取最高优先级的中断,清0 IRR中的对应位,并设置ISR中的对应位,然后ISR中最高优先级的中断被发送到CPU执行(如果其它优先级和屏蔽检查通过)。 4、 执行中断描述符定义的中断处理入口(IDT中指定地址的代码); 5、 根据环境执行不同的中断退出方式,比如执行现场调度操作(retint_careful和retint_kernel),最终都会执行 4、 设置ISA IRQ使用的irq_desc; 5、 把IDT的首地址加载到CPU的IDTR(Interrupt Descriptor Table Register); 6、 初始化中断控制器(下一章描述 edge 触发中断的基本处理过程: 电压跳变触发中断===>中断控制器接收中断,记IRR寄存器===>中断控制器置ISR寄存器===>CPU屏蔽本CPU中断===>CPU处理中断,发出EOI===>中断控制器确认可以处理下一次中断 4、 在外设/驱动中断处理函数层次往往也有中断使能的功能,比如启用了NAPI的网卡,在中断处理函数开始执行的时候,往往会通过硬件功能关闭该中断,要在对应的软中断完成处理后才通过硬件功能使能该中断。
正在处理同一中断的那个CPU完成一次处理后, 会再次检查”触发”标记, 如果设置, 则再次触发处理过程. 于是, 中断的处理是一个循环过程, 每次循环调用handle_IRQ_event来处理中断. 注册的中断处理函数有个中断开关属性, 一般情况下, 中断处理函数总是在关中断的情况下进行的. 而调用request_irq注册中断处理函数时也可以设置该中断处理函数在开中断的情况下进行, 这种情况比较少见, 因为这要求中断处理代码必须是可重入的. 于是, 一个中断处理过程被分成了两部分, 第一部分在中断处理函数里面关中断的进行, 第二部分在软中断处理函数里面开中断的进行. 极端情况下,嵌套发生的软中断可能非常多,全部处理完可能需要很长的时间,于是内核会在处理完一定数量的软中断后,将剩下未处理的软中断推给一个叫ksoftirqd的内核线程来处理,然后结束本次中断处理过程。
在之前的ARMv8-A的异常文章中提到,ARMv8-A将中断也当做一种异常,中断分为IRQ和FIQ 假设当前在EL0运行一个64位的应用程序,触发了一个EL0的IRQ中断,则处理器会做如下的操作 将CPU , #16 * 2] stp x6, x7, [sp, #16 * 3] stp x8, x9, [sp, #16 * 4] stp x10, x11, [sp, #16 * 5] msr daifclr, #(8 | 4 | 1) .endm 跳转到irq_handler去处理中断 当中断处理完毕后,就会通过ret_to_user返回到用户空间 /* * Interrupt isb(); err = handle_domain_irq(gic_data.domain, irqnr, regs); //处理中断 ,调用generic_handle_irq_desc去处理中断,后面会涉及到irq domain的知识了。
2.中断门(Interrupt gate) 其类型码为110,中断门包含了一个中断或异常处理程序所在段的选择符和段内偏移量。 当控制权通过中断门进入中断处理程序时,处理器清IF 标志,即关中断,以避免嵌套中断的发生。 通过系统门来激活4 个Linux 异常处理程序,它们的向量是3、4、5 及128,也就是说,在用户态下,可以使用int 3、into、bound 及int 0x80 四条汇编指令。 13),因为中断处理程序的特权级不能低于引起中断的程序的特权级。 这种情况发生的可能性不大,因为中断处理程序一般运行在内核态,其特权级为0。
在这里简单记录一下目前DragonOS中的中断处理的设计吧。 https://github.com/fslongjin/DragonOS 写这篇文章的时候,代码版本长这样:https://github.com/fslongjin/DragonOS/commit/5ec1b825e6f8b780027b29ec80b8d672dd8884bd * @param irq_num 中断向量号 * @param arg 传递给中断安装接口的参数 * @param handler 中断处理函数 * @param paramater 中断处理函数的参数 2.1软中断向量表表项定义 表项类型定义为softirq_t 成员 类型 参数 返回值 描述 action 函数 void*data void 软中断处理函数 data void* – – 传递给软中断处理函数的数据 unregister_softirq * @param irq_num 软中断号 2.4软中断处理程序do_softirq 在该函数中,先检测软中断是否正在被处理,如果空闲,则发起处理并置位标志位。
缺页中断处理一般流程: 1.硬件陷入内核,在堆栈中保存程序计数器,大多数当前指令的各种状态信息保存在特殊的cpu寄存器中。 2.启动一个汇编例程保存通用寄存器和其他易丢失信息,以免被操作系统破坏。 5.如果选择的页框脏了,则将该页写回磁盘,并发生一次上下文切换,挂起产生缺页中断的进程让其他进程运行直到写入磁盘结束。且回写的页框必须标记为忙,以免其他原因被其他进程占用。 7.当磁盘中断发生时,表明该页已经被装入,页表已经更新可以反映他的位置,页框也标记位正常状态。 8.恢复发生缺页中断指令以前的状态,程序计数器重新指向这条指令。 9.调度引发缺页中断的进程,操作系统返回调用他的汇编例程 10.该例程恢复寄存器和其他状态信息,返回到用户空间继续执行,就好像缺页中断没有发生过。 ,这样不会导致死循环中断,内核设计很安全。
要处理中断,需要有一个中断处理函数。 很多中断处理程序将整个中断要做的事情分成两部分,称为上半部和下半部,或者成为关键处理部分和延迟处理部分。 在中断处理函数中,仅仅处理关键部分,完成了就将中断信号打开,使得新的中断可以进来,需要比较长时间处理的部分,也即延迟部分,往往通过工作队列等方式慢慢处理。 有了中断处理函数,接下来要调用 request_irq 来注册这个中断处理函数。 每一个中断处理动作的结构 struct irqaction,都有以下成员:中断处理函数 handler;void *dev_id 为设备 id;irq 为中断信号;如果中断处理函数在单独的线程运行,则有
以s3c2440 ARM9核为例: 一:s3c2440 ARM处理器特性: 1、S3C2440支持60个中断源,含子中断源; 2、ARM9采用五级流水线方式; 3、支持外部中断和内部中断 INTPND:中断优先级仲裁器选出优先级最高中断后,这个中断在INTPND寄存器中的相应位被置1,随后,CPU进入中断模式处理它。同一时间内,此寄存器只有一位被置1。 三、中断处理流程 1、中断控制器汇集各类外设发出的中断信号,然后通知CPU。 2、CPU保存当前程序的运行环境,然后调用中断服务程序(ISR),来处理中断。 3、在ISR中通过读取外设的相关的寄存器来识别中断的类型,并进行相应的处理。 4、清除中断:通过读写相关中断控制寄存器和外设相关寄存器来实现。 (注意消除中断是必要的) 5、恢复被中断程序的执行环境,继续执行被中断的程序。
有了中断,才能实现多道程序并发执行 概念 中断发生时,CPU立即进入核心态 中断发生后,当前进程暂停运行,并由操作系统内核对中断进行处理 对于不同的中断信号,会进行不同的处理 用户态切换到核心态是通过中断实现的 内中断还细分为 自愿中断:指令中断,如系统调用时的访管指令(陷入指令,trap指令) 强迫中断:硬件故障(如缺页中断),软件中断(如除0) 内中断另一种分类方式: 陷阱,陷入(trap):有意而为之的异常 ,如系统调用 故障(fault):由错误条件引起的,可能被故障处理程序修复,如缺页 终止(abort):不可恢复的致命错误造成的结果,终止处理程序不再将控制返回给引发终止的应用程序,如整数除0操作 外中断 也称为中断(狭义上的中断) 信号来源:CPU内部,与当前执行的指令无关 外中断还可以分为 外设请求:如I/O操作完成发出的中断信号 人工干预:如用户强行停止一个进程 外中断处理过程 CPU在用户态下逐条执行指令 ,在每条指令执行结束后进行检查,检查当前是否有外部中断信号 如果检测到外部中断信号在,则需要保护被中断进程的CPU环境(如程序状态字PSW,程序计数器PC,各种通用寄存器) 根据中断信号类型转入响应的中断处理程序
优先级高的中断可能打断优先级低的中断处理,形成中断嵌套。系统根据中断优先级进行调度处理,确保紧急和重要事件优先得到处理,从而保证系统稳定、安全、高效地运行。 中断延迟指从中断发生到开始执行中断处理程序的时间间隔,响应时间则包括整个中断处理过程的持续时间。优化中断处理可以显著减少延迟,提高系统响应速度。2.中断频率与系统负载中断频率对系统负载有直接影响。 主要的优化策略包括:中断合并:将多个中断请求合并处理,减少上下文切换次数批处理:累积多个事件后一次性处理,降低中断频率硬件辅助:利用硬件特性加速中断识别和响应中断屏蔽:在关键区域屏蔽中断,保证原子性操作三 (DPC)的机制,类似地实现了中断处理的分级处理。 :中断亲和性:将特定中断绑定到特定CPU核心负载均衡:均匀分配中断处理负载中断合并与聚合:减少中断总数NUMA感知:考虑非统一内存访问架构四、中断处理编程实践1.编写中断处理程序的原则编写高质量的中断处理程序需要遵循以下原则
中断服务程序(ISR)是一个小的程序,用来处理具体的数据,其具体的处理方式依赖于造成中断请求(IRQ)的原因。之前正在运行的进程在中断服务程序(ISR)运行结束前都会被中断。 在过去,中断请求由单独的芯片处理(中断控制器芯片 PIC),I/O 设备直接与中断控制器(PIC)相连。中断控制器(PIC)管理着多种硬件的中断请求(IRQ),并且可以直接与 CPU 通信。 现如今,中断请求(IRQ)由 CPU 中的 高级可编程中断控制器(advanced programmable interrupt controller)(APIC)部分来处理。 硬件中断 当一个硬件设备想要告诉 CPU 某一需要处理的数据已经准备好后(例如:当键盘被按下或者一个数据包到了网络接口处),它将会发送一个中断请求(IRQ)来告诉 CPU 数据是可用的。 这是最严重的中断,终止将会调用系统的终止异常处理程序来结束造成异常的进程。 动手实践 中断请求按照高级可编程中断控制器(APIC)中的优先级高低排序(0是最高优先级)。
由于在处理硬件中断服务时会关闭硬件中断,所以在处理硬件中断服务的过程中,如果发生了其他的硬件中断,也不能得到有效的处理,从而导致硬件中断丢失的情况。 为了避免这种情况出现,Linux 内核把中断处理分为:中断上半部 和 中断下半部,上半部在关闭中断的情况下进行,而下半部在打开中断的情况下进行。 由于中断上半部在关闭中断的情况下进行,所以必须要快速完成,从而避免中断丢失的情况。而中断下半部处理是在打开中断的情况下进行的,所以可以慢慢进行。 一般来说,网卡驱动向内核注册的中断处理服务属于 中断上半部,如前面介绍的 NS8390网卡驱动 注册的 ei_interrupt 中断处理服务,而本文主要分析网卡 中断下半部 的处理。 对于 Linux 内核的中断处理机制可以参考我们之前的文章 Linux中断处理,这里就不详细介绍了。在本文中,我们只需要知道网络中断下半部处理例程为 net_rx_action 函数即可。
control(处理中断的嵌套、抢占等),当然最终会遍历该中断描述符的IRQ action list,调用外设的specific handler来处理该中断; d -- 具体CPU architecture 相关的模块会进行现场恢复; 总结下来,整个过程可以分为三部分:1、硬件处理部分;2、汇编处理部分;3、C 处理部分; 下面我们来追踪一下代码,了解当中断发生时,Linux 是如何处理的,前面的一些中断初始化部分就不再这里详述了 ,下面开始具体分析: 一、硬件处理部分 当一切准备好之后,一旦打开处理器的全局中断就可以处理来自外设的各种中断事件了。 //将之前保存在中断模式堆栈中的r0_usr,lr,spsr分别存储到r3-r5中 add r0, sp, #S_PC @ here for interlock avoidance #S_PC= 现在处理中断中我们就看到了调用了我们自己的中断处理函数来处理中断了。
编程处理0号中断 1.1 效果演示 现在我们考虑改变一下0号中断处理程序的功能,即重新编写一个0号中断处理程序,它的功能是在屏幕中间显示“overflow!”然后返回到操作系统,如下图所示。 可见 ,当中断 0 发生时,CPU将转去执行中断处理程序。 1.5 从CPU的角度看中断处理程序 现在,我们在反过来从CPU的角度看一下,什么是中断处理程序? ,即do0 的代码,就变成了0号中断的中断处理程序。 比如:mov ax,8-4,被编译器处理为指令: mov ax,4。 另外,编译器还可以处理表达式。 比如指令: mov ax,(5+3)\*5/10,被编译器处理为指令: mov ax,4。
处理器一经检测到该信号,便中断自己当前正在处理的工作,转而去处理中断。此后,处理器会通知 OS 已经产生中断。这样,OS 就可以对这个中断进行适当的处理。 中断处理 - 上半部(硬中断) 由于 APIC中断控制器 有点小复杂,所以本文主要通过 8259A中断控制器 来介绍Linux对中断的处理过程。 处理中断请求 当一个中断发生时,中断控制层会发送信号给CPU,CPU收到信号会中断当前的执行,转而执行中断处理过程。 处理完中断后,调用 do_softirq() 函数来对中断下半部进行处理(下面会说)。 中断处理 - 下半部(软中断) 由于中断处理一般在关闭中断的情况下执行,所以中断处理不能太耗时,否则后续发生的中断就不能实时地被处理。
在上一篇如何优雅地取消线程任务中提到了通过中断可以取消线程正在进行的任务,现在针对中断这件事情再来简单聊聊。 return true; } finally { lock.unlock(); } } } 传递中断 如果捕获到一个中断异常不知道怎么处理它 ,因为这件事只有一个人来处理就够了,所以抛出异常后会清除中断状态,比如Thread,sleep()。 false 可有人想中断我?false 可有人想中断我? 非中断”,但被我设为中断了 可有人想中断我?
理解它们之间的优先级关系以及特殊的中断处理机制,如中断延迟处理,对于开发高效、稳定且具有良好实时性的嵌入式系统至关重要。本文将详细探讨这些概念,并提供相关代码示例以加深理解。 当一个中断发生时,无论当前正在执行的任务优先级多高,处理器都会立即暂停任务执行,转而进入中断服务程序(ISR)处理中断。 只有在中断处理完成后,才会根据任务优先级来决定是继续执行高优先级任务还是低优先级任务。 三、中断的延迟处理 (一)理论概述 在某些情况下,硬件中断的处理可能非常耗时。 为了解决这个问题,FreeRTOS 采用了中断延迟处理机制。该机制将中断处理分为两部分:在中断服务程序(ISR)中,尽快完成一些必要的清理和记录工作,然后触发一个任务来处理更复杂、耗时的操作。 这样可以保证中断响应的及时性,同时也不会影响系统的整体性能和任务的执行。 (二)代码示例 以下是一个中断延迟处理的代码示例。假设我们有一个串口接收中断,接收大量数据并进行处理。
注册中断服务程序,首先得有中断服务程序是吧,我将 $xv6$ 里的中断服务程序分为三部分: 中断入口程序 中断处理程序 中断退出程序 中断处理程序每个中断是不同的,但是中断入口和中断的出口(退出)是基本是相同的 中断入口程序就是保存上下文,然后跳到真正的中断处理程序执行中断,之后再跳转到中断退出程序。 这里涉及到两个跳,第一个从中断入口程序跳到中断处理程序,一个相同的入口点是如何跳到不同的中断处理程序的呢? 第二跳从中断处理跳到中断退出程序,这其实没什么特殊的处理,中断入口程序和中断退出程序在一个汇编文件里面,中断入口程序调用中断处理程序,中断处理程序执行完成之后自然会回到中断退出程序。 //向量号 // below here defined by x86 hardware uint err; uint eip; ushort cs; ushort padding5; & 0xf) + ((bcd>>4)*10)); } 其实原理很简单,比如 BCD 码表示 15 这个数字,表示方式是:0001 1001,BCD 码是用四位来表示一个数的,前四位表示1,后四位表示 5,
外设的中断信号被送到“通用的中断信号处理模块”和“特定中断信号接收模块”。 正常工作的时候,我们会turn on“通用的中断信号处理模块”的处理逻辑,而turn off“特定中断信号接收模块” 的处理逻辑。 一旦唤醒,我们最好是turn off“特定中断信号接收模块”,让外设的中断处理回到正常的工作模式,同时,也避免了系统suspend-resume模块收到不必要的干扰。 ),而普通中断由于其IRQ被disable了,因此无法唤醒idle状态中的处理器。 整个过程和将系统从suspend状态中唤醒一样,唯一不同的是:将系统从freeze状态唤醒走的中断处理路径,而将系统从suspend状态唤醒走的唤醒处理路径,需要电源管理HW BLOCK中特别的中断处理逻辑的参与