队列处理中断底半部

如果用一个词来形容底半部的功能,就是“延迟执行”

 对于中断底半部的实现方式一共有三种:

采用软中断的方式

采用tasklet微线程

采用队列

 

因为现在碰到的几个中断处理方式都是使用工作队列来实现的,所以这里主要讲解队列:

 一、 工作队列

      Linux内核中,对下半部(或者说推后执行的工作)的处理方式有好几种,包括BHbottom half),软中断,Tasklets和工作队列等等。在2.6内核中,大名鼎鼎的BH处理被废除,新增了更方便的工作队列。工作队列的方便之处在于它把 工作推后,交由一个内核线程去执行,这个内核线程总会在进程上下文执行,因此,它就可以很方便的持有信号量(semaphore),当然也可以允许睡眠

      内核对工作队列的处理是通过工作者线程完成的。工作者线程在一般情况下处于睡眠状态,当我们把需要推迟执行的工作注册到工作队列中之后,唤醒工作者线程会遍历工作队列中的每个待处理的工作,并执行工作队列结构work_struct中的func函数。

这里涉及到两个概念:工作者线程和工作队列。

首先看看我们最关心的工作队列。

      struct work_struct*delayed_work;

      INIT_WORK(delayed_work,delayed_work_handler, data);

      schedule_work(delayed_work);

      delayed_work是我们声明的工作队列;通过INIT_WORK对工作队列进行初始化,delayed_work_handler是工作队列的处 理函数,data是传递给处理函数的参数;最后调用schedule_work唤醒工作者线程处理推后执行的工作。如果需要经过一段延迟以后再执行工作, 可以调用:

      schedule_delayed_work(delayed_work,delay);   //delay 是需要延迟的节拍数

      另外还有一种静态创建工作队列的方式:

      DECLARE_WORK(name, void (*func) (void *),void *data);

      大部分情况下我们了解到这里已经足够了。工作者线程可以放心的交给内核去完成。

对于工作者线程的使用有两种方式,一是直接使用内核中每个CPU对应的一个 缺省工作者线程envents/nn代表CPU的序号,从0开始);

再者就是自己创建一个专用的工作者线程。

对于通常情况下,驱动开发者是不必关心工作者线程的,缺省的工作者线程能够做的很好。如果缺省的队列不能满足要求,自己创建一个工作者线程也很简单,只需要调用:

      structworkqueue_struct *create_workqueue(const char *name);

      调度时使用如下函数:

      intqueue_work(struct workqueue_struct *wq, struct work_struct *work);

      

      intqueue_delayed_work(struct workqueue_struct *wq, struct work_struct

      *work,unsigned long delay);

      它们与schedule_work()以及schedule_delayed_work()是类似的。

 

二、等待队列

 

staticDECLARE_WAIT_QUEUE_HEAD(waiter);      //定义并初始化waiter

   wake_up_interruptible(&waiter);                 //唤醒等待队列

 

   set_current_state(TASK_INTERRUPTIBLE);        

   wait_event_interruptible(waiter, tpd_flag != 0); //判断执行条件

 

 

Linux 工作队列和等待队列的区别:

对这2个容易混淆的队列做简单概念上的区别: 

     等待队列在内核中有很多用途,尤其适合用于中断处理,进程同步及定时。我们在这里只说,进程经常必须等待某些事件的发生。例如,等待一个磁盘操作的终止,等待释放系统资源,或者等待时间经过固定的间隔。 

         等待队列实现了在事件上的条件等待,希望等待特定事件的进程把自己放进合适的等待队列,并放弃控制权。因此。等待队列表示一组睡眠的进程,当某一条件为真时,由内核唤醒它们。 

等待队列由循环链表实现,其元素包括指向进程描述符的指针。每个等待队列都有一个等待队列头,等待队列头是一个类型为wait_queue_head_t的数据结构。 

等待队列链表的每个元素代表一个睡眠进程,该进程等待某一事件的发生,它的描述符地址存放在task字段中。然而,要唤醒等待队列中所有的进程有时并不方便。例如,如果两个或多个进程在等待互斥访问某一个要释放的资源,仅唤醒等待队列中一个才有意义。这个进程占有资源,而其他进程继续睡眠可以用DECLARE_WAIT_QUEUE_HEAD(name)宏定义一个新的等待队列,该宏静态地声明和初始化名为name的等待队列头变量。 init_waitqueue_head()函数用于初始化已动态分配的wait queue head变量等待队列可以通过DECLARE_WAITQUEUE()静态创建,也可以用init_waitqueue_head()动态创建。进程把自己放入等待队列并设置成不可执行状态。 

 

工作队列,workqueue,它允许内核代码来请求在将来某个时间调用一个函数。用来处理不是很紧急事件的回调方式处理方法。

工作队列的作用就是把工作推后,交由一个内核线程去执行,更直接的说就是如果您写了一个函数,而您现在不想马上执行它,您想在将来某个时刻去执行它,那您用工作队列准没错 

如果需要用一个可以重新调度的实体来执行你的下半部处理,也应该使用工作队列。它是唯一能在进程上下文运行的下半部实现的机制,也只有它才可以睡眠。这意味着在需要获得大量的内存时、在需要获取信号量时,在需要执行阻塞式的I/O操作时,它都会非常有用。

猜你喜欢

转载自blog.csdn.net/good123_2014/article/details/73393745