大家好,我是王鸽,这篇文章讲关于Linux中断管理问题,涉及中断硬件框架,软件中断号,通用的中断处理函数等等。什么是中断?
所谓中断是指CPU在执行程序的过程中, 出现了某些突发事件急待处理, CPU必须暂停当前程序的执行, 转去处理突发事件, 处理完毕后又返回原程序被中断的位置继续执行。 首先,中断是分优先级的。很多个设备同时发送中断,处理器必须遵守一定的规则来给所有中断排序,从而决定先响应哪个。这个规则就是中断优先级,操作系统需要维护中断处理器中的优先级寄存器。
既然有了优先级,就有了中断嵌套。当处理器处理某个低优先级的中断时,来了一个高优先级的中断,CPU可以保留低优先级的中断处理现场,转而处理高优先级中断,待高优先级处理完再继续处理低优先级中断。如果CPU在某段时间内,选择不响应某设备,其中断也可以被屏蔽。
Linux 内核需要对连接到计算机上的所有硬件设备进行管理。如果要管理这些设备,首先得和它们互相通信才行。
一般有两种方案可实现这种功能:
轮询(polling) 让内核定期对设备的状态进行查询,然后做出相应的处理;中断(interrupt) 让硬件在需要的时候向内核发出信号(变内核主动为硬件主动)。但是使用轮询的方式会占用CPU比较多的时间,效率极低。例如:要读取一个按键有没有被按下时,一个进程需要不断地查询按键有没有被按下。这样这个任务就占用CPU大量的时间,使得CPU做了大量的无用功。使用中断提供这样的一个机制。当按键没有被按下的时候,挂起当前进程,将控制权转交给其他进程。当按键按下的时候,操作系统把当前进程设为活动的,从而允许该进程继续执行。
中断控制器
外围设备不是把中断请求直接发给处理器,而是发给中断控制器,由ARM架构,使用较多的中断控制器是VIC(Vector Interrupt Controller),进入多核时代,使用较多的是GIC(Generic Interrupt Controller);目前GIC架构规范有4个版本:v1~v4。GIC v2最多支持8个处理器,GIC v3最多支持128个处理器,GIC v3和GIC v4只支持ARM64处理器。GIC 硬件
芯片厂商研发自己的ARM处理器,向ARM公司购买GIC的授权或者是芯片厂商直接向ARM公司购买处理器的授权,这些处理器包含了GIC。软件通过中断号识别中断,每个中断号唯一对应一个中断源。( 1)软件生成的中断( Software Generated Interrupt, SGI):中断号 0~15,通常用来实现处理器间中断( Inter-Processor Interrupt, IPI)。这种中断是由软件写分发器的软件生成中断寄存器( GICD_SGIR)生成的。( 2)私有外设中断( Private Peripheral Interrupt, PPI):中断号 16~31。处理器私有的中断源,不同处理器的相同中断源没有关系,比如每个处理器的定时器。( 3)共享外设中断( Shared Peripheral Interrupt, SPI):中断号 32~1020。这种中断可以被中断控制器转发到多个处理器。(4)局部特定外设中断( Locality-specific Peripheral Interrupt, LPI):基于消息的中断。ARM多核处理器的GIC
GIC中断控制器组成
分别是仲裁单元和cpu interferce模块组成,仲裁单元为每一个中断源维护一个状态机,支持的状态有Inactive,Pending,Active,Active and pending状态。中断无触发、无激活、无挂起,为中断初始 / 终止后的默认状态。中断源已触发中断请求,但 CPU / 处理核心尚未响应,中断等待调度处理。中断已被 CPU 响应,中断服务程序 ISR正在执行,未执行完成。Active and Pending(活跃且挂起)同号中断触发时,该中断的 ISR 仍在执行,中断同时处于活跃 + 挂起叠加态。中断的状态转换过程如下。(1) Inactive -> Pending:外围设备发送了中断。(2) Pending -> Active:CPU 通过中断接口应答中断(读GICC_IAR寄存器获取中断 ID),GIC 确认 CPU 开始处理,状态切换为 Active。(3) Active -> Inactive:处理器处理完中断。处理器可以通过中断控制器的寄存器访问中断控制器。中断控制器的寄存器和物理内存使用统一的物理地址空间,把寄存器的物理地址映射到内核的虚拟地址空间,可以像访问内存一样访问寄存器。所有处理器可以访问公共的分发器,但是每个处理器使用相同的地址只能访问自己私有的处理器接口。GIC控制器
GICV3物理生命周期:
• generate:外设或软件发起一个中断
• distribute:中断经过分组,优先级仲裁等,发送给对应的CPU interface
• deliver:CPU interface将中断发送给core
• activate:当CPU core开始响应中断,GIC将最高激活优先级的中断设置为激活
• priority drop: core发信号给GIC,通知最高优先级中断,GIC可以重置优先级
• deactivation:清除中断
这里要解释一下CPU interface。前面也提到了,core提供给中断的物理管脚只有两个,中断会有成百上千个,GIC怎么把这些中断发送给core是一个问题。这时就需要CPU interface了。CPU interface将GIC发送的中断信息,通过IRQ,FIQ管脚,发送给连接到core。CPU interface提供了以下的功能:
• 将中断请求发送给core
• 中断进行认可
• 中断完成识别
• 设置中断优先级屏蔽
• 定义中断抢占策略
• 决定当前处于pending状态最高优先级中断
在GICv3架构中,CPU interface被抽离出来,实现在core内部的。是不包含CPU interface的。这样做的好处是,可以减少中断响应的时间,并且减少系统总线的占用。对于众核SoC设计来说,其物理设计非常大,CPU interface实现在core内部,也就意味着某些中断寄存器可以放在其内部,这样core就可以很快的访问到这些寄存器了。对于那些常用的寄存器,core不用跋山涉水的通过系统总线或片上网络去频繁访问GIC了。CPU interface与GIC之间,是通过专用的AXI-stream总线来传输信息的。
另外代码位于驱动目录drivers\irqchip目录下保存在各种不同的中断控制器的驱动代码,这个版本的内核支持了GICV3。irq-gic-common.c是通用的GIC的驱动代码,可以被各个版本的GIC使用。irq-gic.c是用于V2版本的GIC controller,而irq-gic-v3.c是用于V3版本的GIC controller。
中断的触发方式
中断可以是边沿触发( edge-triggered),也可以是电平触发( level-triggered)。边沿触发是在电压变化的一瞬间触发,电压由高到低变化触发的中断称为下降沿触发,电压由低到高变化触发的中断称为上升沿触发。电平触发是在高电压或低电压保持的时间内触发,低电压触发的中断称为低电平触发,高电压触发的中断称为高电平触发。中断产生
在硬件电路中,中断的产生发生一般只有两种,分别是:电平触发方式和边沿触发方式。这里以ARM处理器为例,介绍硬件中断的产生流程:
当一个外部设备产生中断,中断信号会沿着中断线到达中断控制器;中断控制器接收到该外部设备的中断信号后首先会检测中断屏蔽寄存器是否屏蔽该中断(一般为INTMASK寄存器);如果没有屏蔽该中断,则设置中断所在的中断请求寄存器位(一般为INTPEND寄存器),并将IRQ拉高用于通知CPU,CPU接收到IRQ中断后,检查CPSR寄存器的I标志位是否禁止了总中断,若禁止了则忽略,然后CPU跳转到中断向量处开始执行中断处理程序;中断的来源, 中断可分为内部中断和外部中断, 内部中断的中断源来自CPU内部(软件中断指令、 溢出、 除法错误等, 例如, 操作系统从用户态切换到内核态需借助CPU内部的软件中断), 外部中断的中断源来自CPU外部, 由外设提出请求。与之对应的还有软中断和硬中断:
硬中断是由外部事件引起的因此具有随机性和突发性;硬中断是否可以嵌套的,是否有优先级,由硬件设计体系决定的
软中断是执行中断指令产生的,无需外部施加中断请求信号,因此中断的发生不是随机的而是由程序安排好的。软中断是一种推后执行的机制。
中断是否可以屏蔽?
中断可分为可屏蔽中断与不可屏蔽中断(NMI) , 可屏蔽中断可以通过设置中断控制器寄存器等方法被屏蔽, 屏蔽后, 该中断不再得到响应, 而不可屏蔽中断不能被屏蔽。根据中断入口跳转方法的不同, 中断可分为向量中断和非向量中断。 采用向量中断的CPU通常为不同的中断分配不同的中断号, 当检测到某中断号的中断到来后, 就自动跳转到与该中断号对应的地址执行。 不同中断号的中断有不同的入口地址。 非向量中断的多个中断共享一个入口地址, 进入该入口地址后, 再通过软件判断中断标志来识别具体是哪个中断。 也就是说, 向量中断由硬件提供中断服务程序入口地址, 非向量中断由软件提供中断服务程序入口地址。中断应用
中断是外围设备通知处理器的一种机制,典型的例子是:网卡从网络收到报文,把报文放到接收环,然后发送中断请求通知处理器,接着处理器响应中断请求,执行中断处理程序,从网卡的接收环取走报文;网卡驱动程序发送报文的时候,把报文放到网卡的发送环,当网卡从发送环取出报文发送的时候,发送中断请求通知处理器发送完成。