分类: LINUX
2011-11-12 17:57:31
首先我们先要了解为什么要使用下半部机制:前面已经说过中断处理程序既要保持快速执行,也就是尽力缩短中断处理程序的执行,还要与其他处理程序异步执行,要保持高效行就要把一些工作推后执行,具体怎么个推后,要理解不是马上执行,而是載中断恢复后执行就OK了。
下半部的实现机制在2.6之前提供了小任务和任务队列来实现,2.6之后包括软中断、tasklet和工作队列。其中tasklet是通过软中断来实现的。下面我们就来分别认识下三种实现机制:
软中断:软中断是載编译期间静态分配的。tasklet是软中断的一个特例。软中断具有可扩展性,如果不需要扩展就成了tasklet。
这这里提到软中断就要和硬中断作一区别:
硬中断是外设对CPU的中断,而软中断是硬中断服务程序对内核的中断。
软中断由softirq_action结构表示,具体原型如下:
struct softirq_action
432 {
433 void (*action)(struct softirq_action *);
434 };
435
软中断的处理程序函数原型如下:
void softirq_handler(struct softirq_action *)
当内核运行一个软中断处理程序的时候,它就会执行这个action函数,其唯一的参数为指向相应的softirq_action 的结构体。
软中断的注册通过void (int , void (*)(struct *))
相应的两个参数:软中断的索引号和处理函数。
tasklet是利用软件中断实现的一种下半部机制,相对与其他,它的接口更为简单,且经常被使用。tasklet又叫小任务。
Tasklet结构体
tasklet由tasklet_struct结构体表示,具体定义如下:
struct tasklet_struct
497 {
498 struct tasklet_struct *next; //指向链表中的下一个结构
499 unsigned long state; //小任务的状态
500 atomic_t count; //引用计数器
501 void (*func)(unsigned long); //小任务的处理函数
502 unsigned long data; //传递给小任务处理函数的参数
503 };
小任务既可以静态的创建也可以动态的创建,静态的创建一个小任务,则任选下面两个宏之一即可:
#define DECLARE_TASKLET(name, func, data) \
506 struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
#define DECLARE_TASKLET_DISABLED(name, func, data) \
509 struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }
大家估计看出了两个宏的区别,第一个的引用计数器设置为0,表示小任务处于激活状态,而另一个置1,则说明小任务处于禁止状态。具体创建如下:
struct tasklet_struct my_tasklet={ NULL, 0, ATOMIC_INIT(0), tasklet_handler, dev };
上面提到了tasklet_handler,这也就是自己编写的一个小任务处理程序,函数类型如下:void tasklet_handler,(unsigned long data)
注意:小任务不能睡眠,所以不能載小任务中使用信号量或者其他产生阻塞的函数,但是小任务可以相应中断。
小任务的调度:
小任务被调度之后,已有机会它就会运行。调度函数如下:
static inline void tasklet_schedule(struct tasklet_struct *t)
543 {
544 if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
545 __tasklet_schedule(t);
}
tasklet_schedule主要是通过 __tasklet_schedule来实现。这也体现了代码复用的思想。
调度完成之后小任务有时候还需要被激活,这可以通过 tasklet_enable函数实现。