1、软中断
为了不引起误解,首先总结一下各种响应方式和中断机制。
(1) 硬中断: 由外部设备对CPU产生的中断。
(2) 信号: 由内核或者其他进程对某一个进程的中断。
(3) 软中断: 在Linux内核中,软中断可能会表达两种不同的方式。
- A.有时候我们会表述为通过软中断(ARM为swi)陷入内核,这个时候所说的软中断是指通过软件指令使得CPU引发中断事件。
- B.另一种软中断是:实现中断下半部的一种处理机制。 本文所述重点为实现中断下半部的软中断处理机制 。
(4) 软中断 (softirq)是一种传统的中断下半部处理机制。它执行的时机通常是中断上半部返回的时候,它运行于中断上下文。
(5) 软中断 (softirq)的描述:在Linux Kernel中,使用struct
softirq_action结构体来描述一个软中断,其原型如下:
如上图所示,这个结构体中包含软中断处理函数指针action和传递给该函数的参数。
(6) 软中断的注册 :在Linux Kernel中,使用接口open_softirq()函数来注册一个软中断所对应的处理函数,其原型如下:
- nr:表示软中断向量编号。
- action:表示软中断所对应的处理函数。
在Linux内核中用到的中断向量如下图所示:路径为: linux-3.0.8/include/linux/interrupt.h
如上图所定义的软中断向量是Linux内核中使用在各种情况下的软中断(softirq)。一般来说,在Linux内核驱动开发过程中,不会也不适宜直接使用软中断(softirq)。
(7) 软中断的触发
在Linux Kernel中,可以通过函数接口raise_softirq()来触发一个软中断,其原型如下:
(8) 软中断的使能和失能
在Linux内核中,通过使用local_bh_enable接口函数来使能软中断,通过使用local_bh_disable接口函数来失能软中断。其原型如下:
(9) 软中断的处理
在Linux内核中,通过使用函数__do_softirq()来一次性的按照向量表从高到低循环处理所有软中断,并且软中断不可嵌套。
__do_softirq()的调用时机:
A、 irq_exit() ,硬件中断处理完成后,返回时调用。
B、 ksoftirq() ,内核进程。
C、 local_bh_enable() 时,发现有待处理的软中断,且当时没处在软、硬中断的上下文。
(10)软中断和tasklet
运行于软中断上下文,任然属于原子上下文操作的一种,与工作队列进行对比,工作队列运行于进程上下文。因此软中断和tasklet处理函数不能睡眠,而在工作队列处理函数中允许睡眠。
2、内核定时器
软件意义上实现的定时器实际上还是需要依赖于硬件定时器来实现,Linux内核在时钟中断发生后执行检测各定时器是否到期,到期后的定时器处理函数将作为软中断在中断下半部执行。这时候,时钟中断处理程序会唤起TIMER_SOFTIRQ软中断,然后运行当前处理器上到期的所有定时器。
在Linux内核驱动开发时,我们可以利用Linux内核中提供的操作软件定时器的函数和描述软件定时器的数据结构来完成定时触发工作或者完成周期性的事件。而开发工程师不需要关心这个软件定时器的在内核和硬件上的实现方式。
Linux内核通过struct timer_list结构体来描述一个软件定时器实例。如下图:
如上图所示,当定时器到期/溢出之后,定时器处理函数function会被执行,成员data作为参数被传入到function中;成员expires表示定时器到期的时间(jiffies)。
每一个struct timer_list结构体的示例对应一个内核定时器。
(1)初始化定时器
在Linux内核中,使用接口init_timer()来实现初始化一个内核定时器,其原型如下:
如上图可知,使用init_timer函数初始化时,timer_list的entry的next为NULL并且给base指针赋值。
初始化时需要对timer_list结构体的成员进行赋值,在Linux内核中,提供了一个用于赋值定时器结构体timer_list的function、expires、data和base成员的宏TIMER_INITIALIZER(_function,
_expires, _data) ,其原型如下图:
Linux内核也提供了一种非常快捷的方式来定义并按照默认初始化timer_list结构体成员的宏定义DEFINE_TIMER(_name,
_function, _expires, _data),其中_name为所定义变量名称。其原型如下:
除此之外,setup_timer()函数也可以用于初始化定时器并且为其结构成员进行赋值,其原型为:
(2)增加定时器
Linux内核中提供了add_timer()函数接口用于注册内核定时器,将定时器加入到内核动态定时器链表中。其原型如下:
(3)删除定时器
在Linux内核中,提供了int del_timer(struct timer_list *timer)函数接口用于删除一个内核定时器,其原型如下:
实际上Linux内核还提供了del_timer()函数的同步版本int del_timer_sync(struct timer_list
*timer)函数,它和del_timer()函数接口的区别在于,del_timer_sync()在删除一个定时器时需要等待其被处理完成,所以del_timer_sync()函数不能在中断上下文调用。
(4)修改定时器的expires
可以通过修改timer_list成员expires的值,从而可以修改定时器的到期时间。在新的被传入的expires到来后才会执行定时器函数(定时到期)。
更多Linux教程请查看菜单