中断处理 - 上半部(硬中断) 由于 APIC中断控制器 有点小复杂,所以本文主要通过 8259A中断控制器 来介绍Linux对中断的处理过程。 中断处理相关结构 前面说过,8259A中断控制器 由两片 8259A 风格的外部芯片以 级联 的方式连接在一起,每个芯片可处理多达 8 个不同的 IRQ(中断请求),所以可用 IRQ 线的个数达到 15 注册中断处理入口 在内核中,可以通过 setup_irq() 函数来注册一个中断处理入口。 处理中断请求 当一个中断发生时,中断控制层会发送信号给CPU,CPU收到信号会中断当前的执行,转而执行中断处理过程。 中断处理 - 下半部(软中断) 由于中断处理一般在关闭中断的情况下执行,所以中断处理不能太耗时,否则后续发生的中断就不能实时地被处理。
1、 8259A: 每个8259A有8个管脚,每个管脚对应其连接的CPU的IDT中的一个vector,单独使用8259A,其硬件连线就决定了对设备vector的使用。 NT#模式的局限 1、 中断数量有限且不方便扩展:每个物理的PCI设备,最多只有4个中断但是至少能支持8个function,且系统中可能存在多个PCI设备,不得不使用中断共享的模式,影响使用性能。 edge 触发中断的基本处理过程: 电压跳变触发中断===>中断控制器接收中断,记IRR寄存器===>中断控制器置ISR寄存器===>CPU屏蔽本CPU中断===>CPU处理中断,发出EOI===>中断控制器确认可以处理下一次中断 那么在ISR处理开始的时候,A会告诉ISR,不是它干的,然后ISR处理B的中断,完成后通过清理中断源把B的电压归位,但是由于A的中断没有得到处理,电压没有归位,这个共享的中断就不能得到再次触发了。 4、 在外设/驱动中断处理函数层次往往也有中断使能的功能,比如启用了NAPI的网卡,在中断处理函数开始执行的时候,往往会通过硬件功能关闭该中断,要在对应的软中断完成处理后才通过硬件功能使能该中断。
正在处理同一中断的那个CPU完成一次处理后, 会再次检查”触发”标记, 如果设置, 则再次触发处理过程. 于是, 中断的处理是一个循环过程, 每次循环调用handle_IRQ_event来处理中断. 注册的中断处理函数有个中断开关属性, 一般情况下, 中断处理函数总是在关中断的情况下进行的. 而调用request_irq注册中断处理函数时也可以设置该中断处理函数在开中断的情况下进行, 这种情况比较少见, 因为这要求中断处理代码必须是可重入的. 于是, 一个中断处理过程被分成了两部分, 第一部分在中断处理函数里面关中断的进行, 第二部分在软中断处理函数里面开中断的进行. 极端情况下,嵌套发生的软中断可能非常多,全部处理完可能需要很长的时间,于是内核会在处理完一定数量的软中断后,将剩下未处理的软中断推给一个叫ksoftirqd的内核线程来处理,然后结束本次中断处理过程。
在之前的ARMv8-A的异常文章中提到,ARMv8-A将中断也当做一种异常,中断分为IRQ和FIQ 假设当前在EL0运行一个64位的应用程序,触发了一个EL0的IRQ中断,则处理器会做如下的操作 将CPU stp x2, x3, [sp, #16 * 1] stp x4, x5, [sp, #16 * 2] stp x6, x7, [sp, #16 * 3] stp x8, 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的知识了。
编号,即赋予一个中断类型码 n,Intel 把这个8 位的无符号整数叫做一个向量,因此,也叫中断向量。 2、外设可屏蔽中断、异常及非屏蔽中断 Intel x86 通过两片中断控制器8259A 来响应15 个外中断源,每个8259A 可管理8 个中断源。 在实模式下,中断向量表中的表项由8 个字节组成,如图3.2 所示,中断向量表也改叫做中断描述符表IDT(Interrupt Descriptor Table)。 当控制权通过中断门进入中断处理程序时,处理器清IF 标志,即关中断,以避免嵌套中断的发生。 13),因为中断处理程序的特权级不能低于引起中断的程序的特权级。
在这里简单记录一下目前DragonOS中的中断处理的设计吧。 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.启动一个汇编例程保存通用寄存器和其他易丢失信息,以免被操作系统破坏。 7.当磁盘中断发生时,表明该页已经被装入,页表已经更新可以反映他的位置,页框也标记位正常状态。 8.恢复发生缺页中断指令以前的状态,程序计数器重新指向这条指令。 9.调度引发缺页中断的进程,操作系统返回调用他的汇编例程 10.该例程恢复寄存器和其他状态信息,返回到用户空间继续执行,就好像缺页中断没有发生过。 ,这样不会导致死循环中断,内核设计很安全。 builtin_constant_p(n)) { unsigned long ret; switch (n) { //要复制字节个数 case 1: __get_user_size(*(u8
要处理中断,需要有一个中断处理函数。 很多中断处理程序将整个中断要做的事情分成两部分,称为上半部和下半部,或者成为关键处理部分和延迟处理部分。 在中断处理函数中,仅仅处理关键部分,完成了就将中断信号打开,使得新的中断可以进来,需要比较长时间处理的部分,也即延迟部分,往往通过工作队列等方式慢慢处理。 有了中断处理函数,接下来要调用 request_irq 来注册这个中断处理函数。 每一个中断处理动作的结构 struct irqaction,都有以下成员:中断处理函数 handler;void *dev_id 为设备 id;irq 为中断信号;如果中断处理函数在单独的线程运行,则有
以s3c2440 ARM9核为例: 一:s3c2440 ARM处理器特性: 1、S3C2440支持60个中断源,含子中断源; 2、ARM9采用五级流水线方式; 3、支持外部中断和内部中断 EINTMASK:这个简单,是屏蔽中断用的,也就是说位为1时,此次中断无效。 2.2 内部中断寄存器 内部中断有8个寄存器。 INTPND:中断优先级仲裁器选出优先级最高中断后,这个中断在INTPND寄存器中的相应位被置1,随后,CPU进入中断模式处理它。同一时间内,此寄存器只有一位被置1。 三、中断处理流程 1、中断控制器汇集各类外设发出的中断信号,然后通知CPU。 2、CPU保存当前程序的运行环境,然后调用中断服务程序(ISR),来处理中断。 3、在ISR中通过读取外设的相关的寄存器来识别中断的类型,并进行相应的处理。 4、清除中断:通过读写相关中断控制寄存器和外设相关寄存器来实现。
优先级高的中断可能打断优先级低的中断处理,形成中断嵌套。系统根据中断优先级进行调度处理,确保紧急和重要事件优先得到处理,从而保证系统稳定、安全、高效地运行。 中断延迟指从中断发生到开始执行中断处理程序的时间间隔,响应时间则包括整个中断处理过程的持续时间。优化中断处理可以显著减少延迟,提高系统响应速度。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 函数即可。
相关的模块会进行现场恢复; 总结下来,整个过程可以分为三部分:1、硬件处理部分;2、汇编处理部分;3、C 处理部分; 下面我们来追踪一下代码,了解当中断发生时,Linux 是如何处理的,前面的一些中断初始化部分就不再这里详述了 ,下面开始具体分析: 一、硬件处理部分 当一切准备好之后,一旦打开处理器的全局中断就可以处理来自外设的各种中断事件了。 mrs lr, spsr str lr, [sp, #8] @ save spsr @ @ Prepare for SVC32 mode. .long __irq_invalid@ 5 .long __irq_invalid@ 6 .long __irq_invalid@ 7 .long __irq_invalid@ 8 现在处理中断中我们就看到了调用了我们自己的中断处理函数来处理中断了。
前面我们学习了ATmega8的I/O口作为通用数字输入/输出口来用时对LED数码管控制和扫描按键的应用; 但ATmega8多数的I/O口都是复用口,除了作为通用数字I/O使用,还有其第二功能,这里我们学习 1.外部中断的特点: PD2端口是外部中断源0,PD3端口是外部中断源1。ATmega8的外部中断就是由这两个引脚触发的。 扩展: 中断向量表:Atmega8共有18 个中断源,Flash程序存储器空间的最低位置(0x000—0x012)定义为复位和中断向量空间,也就是说把中断函数的地址保存在这里,当中断发生后就到这里找到对应函数的地址 52 delay_ms(1); 53 } 54 } 3.中断触发键盘扫描 按下键盘的任意一个按键就触发一个中断,然后在中断函数中来调用键盘处理函数。 7 //按键扫描函数,返回按键的值 8 //unsigned char ScanKey(void)函数的实现与上一实例类似 9 10 //中断函数,设置一个标志,表示按键按下 11 //指明中断程序入口地址
编程处理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 就可以对这个中断进行适当的处理。 每个芯片可处理多达 8 个不同的 IRQ。因为从 PIC 的 INT 输出线连接到主 PIC 的 IRQ2 引脚,所以可用 IRQ 线的个数达到 15 个,如图下所示。 另外一个重要的部分是 I/O APIC,主要是收集来自 I/O 装置的 Interrupt 信号且在当那些装置需要中断时发送信号到本地 APIC,系统中最多可拥有 8 个 I/O APIC。 终止 不可恢复的错误 同步 不会返回 X86 体系结构的每个中断都被赋予一个唯一的编号或者向量(8 位无符号整数)。 中断处理相关结构 前面说过,8259A中断控制器 由两片 8259A 风格的外部芯片以 级联 的方式连接在一起,每个芯片可处理多达 8 个不同的 IRQ(中断请求),所以可用 IRQ 线的个数达到 15
在上一篇如何优雅地取消线程任务中提到了通过中断可以取消线程正在进行的任务,现在针对中断这件事情再来简单聊聊。 return true; } finally { lock.unlock(); } } } 传递中断 如果捕获到一个中断异常不知道怎么处理它 ,因为这件事只有一个人来处理就够了,所以抛出异常后会清除中断状态,比如Thread,sleep()。 false 可有人想中断我?false 可有人想中断我? 非中断”,但被我设为中断了 可有人想中断我?
当一个中断发生时,无论当前正在执行的任务优先级多高,处理器都会立即暂停任务执行,转而进入中断服务程序(ISR)处理中断。 ,如记录按键按下次数等 // 由于中断优先级高于任务,此时任务不会执行 // 例如: // static uint8_t keyPressCount = 0; // keyPressCount 只有在中断处理完成后,才会根据任务优先级来决定是继续执行高优先级任务还是低优先级任务。 三、中断的延迟处理 (一)理论概述 在某些情况下,硬件中断的处理可能非常耗时。 为了解决这个问题,FreeRTOS 采用了中断延迟处理机制。该机制将中断处理分为两部分:在中断服务程序(ISR)中,尽快完成一些必要的清理和记录工作,然后触发一个任务来处理更复杂、耗时的操作。 定义任务堆栈大小 #define SERIAL_RX_TASK_STACK_SIZE 256 // 任务句柄 TaskHandle_t serialRxTaskHandle; // 串口接收缓冲区 uint8_
外设的中断信号被送到“通用的中断信号处理模块”和“特定中断信号接收模块”。 正常工作的时候,我们会turn on“通用的中断信号处理模块”的处理逻辑,而turn off“特定中断信号接收模块” 的处理逻辑。 一旦唤醒,我们最好是turn off“特定中断信号接收模块”,让外设的中断处理回到正常的工作模式,同时,也避免了系统suspend-resume模块收到不必要的干扰。 ),而普通中断由于其IRQ被disable了,因此无法唤醒idle状态中的处理器。 整个过程和将系统从suspend状态中唤醒一样,唯一不同的是:将系统从freeze状态唤醒走的中断处理路径,而将系统从suspend状态唤醒走的唤醒处理路径,需要电源管理HW BLOCK中特别的中断处理逻辑的参与
1、armv8中断、系统调用的入口在arch/arm64/kernel/entry.S ENTRY(vectors) ventry el1_sync_invalid // Synchronous EL1t el1_sync:当前处于内核态时,发生了指令执行异常、缺页中断(跳转地址或者取地址)。 el1_irq:当前处于内核态时,发生硬件中断。 el0_sync:当前处于用户态时,发生了指令执行异常、缺页中断(跳转地址或者取地址)、系统调用。 el0_iqr:当前处于用户态时,发生了硬件中断。 我们可以看到 .macro kernel_exit, el, ret = 0,还有一个参数ret,只有在el0_sync处理系统调用时会被置成1。 如果处理系统调用x0存放的是系统的调用的返回值,所以不需要从堆栈中恢复。