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

LinuxIRQ中断子系统浅析

时间:2023-03-14 23:34:38 科技观察

本文以Linux中断子系统架构为视角,旨在提供对Linux中断系统的全局理解,不涉及具体的实现细节。1.Linux中断子系统架构在Linux中断子系统(genericirq)出现之前,内核使用__do_IRQ来处理所有中断,这意味着__do_IRQ必须处理各种类型的中断,这会增加软件的复杂度,层次不清晰,代码的复用性不好。通用中断子系统的雏形最早出现在ARM系统中。一开始kernel的开发者区分了三种中断(levelinterrupts,edgeinterrupts,andsimpleinterrupts),后来回应了eoi(endofinterrupt)中断控制器,添加了fasteoi类型,添加了percputypeforsmp.将这些不同的中断类型抽象出来后,就成为了中断子系统的流程控制层。为了使这部分代码可以被所有架构复用,中断控制器被进一步封装,在中断子系统中形成一个芯片级的硬件封装层。2.芯片级硬件封装层中断系统与CPU硬件密切相关。linux系统为了兼容各种类型的CPU,提供了各种CPU特性和中断控制器的底层封装,使得底层硬件得以实现。尽可能隐藏,让驱动开发者不用关注底层实现。这部分的主要工作是:实现不同CPU的中断入口,初始化中断向量表,这部分通常由汇编实现。为中断控制器实现软件抽象(structirq_chip)。源码路径如下:“arch/arm/plat-s3c24xx/irq.c”在这部分的初始化过程中,系统根据设备使用的中断控制器类型实现了irq_chip结构体。接口,并在irq_desc.irq_data.chip字段中注册irq_chip实例,这样每个irq都与中断控制器相关联。只要知道irq号,就可以得到对应的irq_desc结构,然后就可以通过芯片指针控制器访问中断了。其初始化过程如下图所示:3.中断流控制层由linux内核提供。所谓中断流控,就是对连续不断的中断进行合理、正确的处理,什么时候屏蔽中断,什么时候使能中断,什么时候响应中断控制器等一系列操作。该层实现独立于系统和硬件的中断流控制处理操作。它针对不同的中断电类型(level,edge...)实现了相应的标准中断流程控制处理函数,在这些处理函数的过程中,会将中断的控制权传递给传入的处理函数或者中断线程当驱动程序注册中断时。目前通用的中断子系统实现了如下标准的流控回调函数,定义在:“kernel/irq/chip.c”中,handle_simple_irq用于简单的流控处理;handle_level_irq用于level触发的中断流控处理;handle_edge_irq用于边沿触发中断的流控处理;handle_fasteoi_irq用于需要响应eoi的中断控制器;handle_percpu_irq用于只响应单个cpu的中断;handle_nested_irq用于处理使用线程的嵌套中断;下面的时序图展示了整个通用中断子系统的中断响应过程。flow_handle一栏是中断流控制层的生命周期:4.中断驱动接口层由Linux内核提供。驱动开发者通常只用这一层提供这些接口就可以完成驱动的开发,其他细节都被其他几个软件层很好地“隐藏”,驱动开发者不需要关注底层实现。该部分提供了一系列对驱动程序的编程,用于向系统申请/释放中断、打开/关闭中断、设置中断类型和中断唤醒系统特性等操作。常用接口如:lrequest_irq(unsignedintirq,irq_handler_thandler,unsignedlongirqflags,constchar*devname,void*dev_id)用于向Linux请求中断。irq是要申请的硬件中断号。handler是向系统注册的中断处理程序。irqflags是中断处理的一个属性,一般用来指定对应的中断流控。devname设置中断名称,一般在cat/proc/interrupts中可以看到。dev_id在中断共享的时候会用到,一般设置为这个设备的设备结构或者NULL。enable_irq(unsignedintirq)用于启用中断。disable_irq(unsignedintirq)用于关闭中断。irq_set_chip(irq,*chip)设置中断控制器irq_set_handler(irq,handle)设置中断流控制中断子系统内部定义了几个重要的数据结构,这些数据结构的每个字段控制或影响中断子系统和每个irq的行为和实现。例如:irq_desc、irq_chip、irq_data、irqaction等。其中irq_desc[NR_IRQS]数组是linux内核中用来维护IRQ资源的管理单元。它记录了某个IRQ号对应的流控处理函数、中断控制器、中断服务程序、IRQ自身的属性、资源等,是kernel中断子系统的一个核心数组,中断驱动接口“request_irq()”是修改数组实现中断的注册。5.中断驱动设计有了前面几层的贡献,我们设计linux中断驱动就变得异常简单了。一般情况下,我们只需要使用“request_irq”函数向内核注册相应的中断号及其中断服务程序,然后调用“enable_irq”和“disable_irq”来启用或禁用中断即可。其过程如下图所示:6.中断服务程序设计当CPU接收到中断时,会执行相应的中断服务程序。我们知道CPU在执行中断服务程序的时候是不能执行其他程序的,甚至这个时候CPU也不能响应某个程序。有些优先级比它低的中断,如果CPU长时间执行一个中断服务程序,势必会影响系统的响应速度,降低系统性能。为此,Linux中断子系统将中断分为中断上下文和中断上下文。中断上下文用来执行一些紧急的程序,中断上下文用来执行一些非紧急的可以推迟的程序。Linux提供了三种机制来处理中断上下文:Softirq(软中断)、Tasklet、work_queue(工作队列)。?软中断?Tasklet?Work_queue