linux中断申请函数:
中断函数的处理:
因为在Linux内核中,中断线的数目是有限的,如果每一个设备都使用一根中断线的话,中断线肯定是不够的,所以Linux内核就提出了共享中断这一理念。这里理念的主要目的就是可以在一根中断线上搭载多个中断设备。
其实对于Linux内核来说,要判断其来自哪一个设备,其需要做两步工作。当一个中断来临时,Linux内核会遍历该中断线上所有注册了的中断处理程序,在该中断处理程序中,就会迅速判断到底是来自于哪一个硬件设备。
而在中断程序中如何来判断呢?这就需要相应产生中断的硬件设备来支持了。例如可能中断处理程序会检查一下该处理程序对应的硬件设备的某一寄存器的状态来判断是否该设备发生了中断,如果是该设备发出的中断,就执行接下来的处理函数。如果不是,就立即返回(应该返回IRQ_RETVAL(IRQ_NONE))。
如果我们要申请共享中断函数的话,flag标志位必须还要指定一个IRQF_SHARED(即flag再“ | ”上一个IRQF_SHARED)
do_IRQ是中断在C语言的总入口。
中断子系统内部定义了几个重要的数据结构,这些数据结构的各个字段控制或影响着中断子系统和各个irq的行为和实现方式。例如:irq_desc,irq_chip,irq_data,irqaction,等等。
其中 irq_desc[NR_IRQS]数组是linux内核中用于维护IRQ资源的管理单元,它记录了某IRQ号对应的流控处理函数,中断控制器、中断服务程序、IRQ自身的属性、资源等。
是内核中断子系统的一个核心数组,中断驱动接口“request_irq()”就是通过修改该数组以实现中断的注册
调用do_softirq函数,就说明程序已经进入软中断环境了。与do_IRQ所处的中断的顶半部不同,处于软中断环境中,是可以被其他中断程序打断的,甚至是处于同一中断线的中断,也因为此,所以软中断可以执行一些稍微时间长一点的任务,也不会迟滞系统对中断的反应时间。
我们需要使用软中断时,首先要想Linux内核注册软中断:
我个人理解:
1、do_IRQ里面会调用request_irq注册的中断处理函数。 在终端处理函数里面可以实现tasklet、softirq、线程机制实现中断下半部分。
2、如果中断处理函数中挂起softirq,在do_IRQ中会调用irq_exit函数,irq_exit函数会调用__do_softirq 函数, __do_softirq函数中检查__softirq_pending状态,看是否用softirq被挂起,如果有就会执行soft_irq,而且会不断的检查__softpending状态,一直到所有的softirq被执行完。
实际上即以软中断类型nr作为偏移量置位每cpu变量irq_stat[cpu_id]的成员变量__softirq_pending,这也是同一类型软中断可以在多个cpu上并行运行的根本原因。
注册软中断
void open_softirq(int nr, void (*action)(struct softirq_action *))
触发软中断
void raise_softirq(unsigned int nr)
执行软中断
do_softirq-->__do_softirq
3、tasklet
tasklet_init()函数,初始化tasklet_struct 结构体,里面包含中断下半部处理函数和函数参数。 该部分一般在probe函数中做
tasklet_schedule:把tasklet_struct加入队列中
struct tasklet_struct {
struct tasklet_struct next; /指向链表中的下一个结构/
unsigned long state; / 小任务的状态 /
atomic_t count; / 引用计数器 */
void (func) (unsigned long); / 要调用的函数 /
unsigned long data; / 传递给函数的参数 */
};
结构中的func域就是下半部中要推迟执行的函数 ,data是它唯一的参数。
State域的取值为TASKLET_STATE_SCHED或TASKLET_STATE_RUN。TASKLET_STATE_SCHED表示小任务已被调度,正准备投入运行,TASKLET_STATE_RUN表示小任务正在运行。TASKLET_STATE_RUN只有在多处理器系统上才使用,单处理器系统什么时候都清楚一个小任务是不是正在运行(它要么就是当前正在执行的代码,要么不是)。
Count域是小任务的引用计数器。如果它不为0,则小任务被禁止,不允许执行;只有当它为零,小任务才被激活,并且在被设置为挂起时,小任务才能够执行。
softirq_init:在start_kernel()进行系统初始化中,调用了softirq_init()函数对tasklet_hi_action和tasklet_action两个软中断进行了初始化
tasklet_action:读取tasklet_struct中count和state,然后调用软中断处理函数。
tasklet_disable(&my_tasklet); /* 小任务现在被禁止,这个小任务不能运行 /
tasklet_enable(&my_tasklet); / 小任务现在被激活 */
4、 工作队列(work queue)是另外一种将工作推后执行的形式,它和前面讨论的tasklet有所不同。工作队列可以把工作推后,
交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。
这样,通过工作队列执行的代码能占尽进程上下文的所有优势。最重要的就是工作队列允许被重新调度甚至是睡眠。