本文共 2582 字,大约阅读时间需要 8 分钟。
本文以Linux中断子系统架构为视角,旨在提供一个对Linux中断系统的全局认识,不涉及具体实现细节。
一、Linux中断子系统架构
在Linux中断子系统(genericirq)出现之前,内核使用__do_IRQ处理所有的中断,这意味着__do_IRQ中要处理各种类型的中断,这会导致软件的复杂性增加,层次不分明,而且代码的可重用性也不好。通用中断子系统的原型最初出现于ARM体系中,一开始内核的开发者们把3种中断类型区分出来(电平中断、边缘中断、简易中断),后来又针对某些需要回应eoi(endof interrupt)的中断控制器,加入了fasteoi type,针对smp加入了percpu type。把这些不同的中断类型抽象出来后,成为了中断子系统的流控层。要使所有的体系架构都可以重用这部分的代码,中断控制器也被进一步地封装起来,形成了中断子系统中的芯片级硬件封装层。
二、芯片级硬件封装层
l
l
该部分初始化过程中,系统根据设备使用的中断控制器的类型,实现irq_chip结构中的接口,并把该irq_chip实例注册到irq_desc.irq_data.chip字段中,这样各个irq和中断控制器就进行了关联,只要知道irq编号,即可得到对应到irq_desc结构,进而可以通过chip指针访问中断控制器。
三、中断流控层
目前的通用中断子系统实现了以下这些标准流控回调函数,这些函数都定义在:”kernel/irq/chip.c”中,
以下这个序列图展示了整个通用中断子系统的中断响应过程,flow_handle一栏就是中断流控层的生命周期:
四、中断驱动接口层
由linux内核提供,驱动程序的开发者通常只会使用到这一层提供的这些接口即可完成驱动程序的开发工作,其他的细节都由另外几个软件层较好地“隐藏”起来了,驱动程序开发者无需再关注底层的实现。该部分向驱动程序提供的一系列的编程,用于向系统申请/释放中断,打开/关闭中断,设置中断类型和中断唤醒系统的特性等操作。常用的一些接口如:
request_irq(unsignedint irq, irq_handler_t handler,
用来向Linux申请中断。
irq是要申请的硬件中断号。
handler是向系统注册的中断处理函数。
irqflags是中断处理的属性,一般用来指定相应的中断流控。
devname设置中断名称,通常是在cat/proc/interrupts中可以看到此名称。
dev_id在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。
l
用来打开中断。
l
用来关闭中断。
l
设置中断控制器
l
设置中断流控
中断子系统内部定义了几个重要的数据结构,这些数据结构的各个字段控制或影响着中断子系统和各个irq的行为和实现方式。例如:irq_desc,irq_chip,irq_data,irqaction,等等。其中
五、中断驱动程序设计
有了前几层所做的贡献,使得我们进行linux中断驱动设计变得异常简单。一般情况下,我们只需要使用”request_irq”函数向内核注册相应的中断号及其中断服务程序,然后调用“enable_irq”“disable_irq”开开或关闭中断即可。其流程如下图所示:
六、中断服务程序设计
当CPU收到中断,就会执行相应中断服务程序,我们知道CPU在执行中断服务程序时是不能执行其他程序的,甚至此时CPU不能响应某些优先级比它低的中断,如果CPU一直长时间执行某个中断服务程序,势必影响系统的响应速度,降低了系统性能。为此Linux中断子系统将中断分为了中断上文和中断下文,中断上文用来执行一些紧迫的程序,中断下文用来执行一些不紧急的可延后执行的程序。Linux提供了三种机制来处理中断下文:Softirq(软中断)、Tasklet、work_queue(工作队列)。
Ø
Ø
Ø