Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4264009
  • 博文数量: 776
  • 博客积分: 13014
  • 博客等级: 上将
  • 技术积分: 10391
  • 用 户 组: 普通用户
  • 注册时间: 2010-02-22 17:00
文章分类

全部博文(776)

文章存档

2015年(55)

2014年(43)

2013年(147)

2012年(20)

2011年(82)

2010年(429)

分类: LINUX

2011-02-14 15:09:57

工作队列接口是在2.5的开发过程中引入的,用于取代任务队列接口(用于调度内核任务)。每个工作队列有一个专门的线程,所有来自运行队列的任务在进程的上下文中运行(这样它们可以休眠)。驱动程序可以创建并使用它们自己的工作队列,或者使用内核的一个工作队列。工作队列用以下方式创建:

  struct workqueue_struct *create_workqueue(const char *name);
  在这里 name 是工作队列的名字。

  工作队列任务可以在编译时或者运行时创建。任务需要封装为一个叫做 work_struct 的结构体。在编译期初始化一个工作队列任务时要用到:

  DECLARE_WORK(name, void (*function)(void *), void *data);
  在这里 name 是 work_struct 的名字,function 是当任务被调度时调用的函数,data 是指向那个函数的指针。

  在运行期初始化一个工作队列时要用到:

  INIT_WORK(struct work_struct *work, void (*function)(void *), void *data);
  用下面的函数调用来把一个作业(一个类型为work_struct 结构的工作队列作业/任务)加入到工作队列中:

  int queue_work(struct workqueue_struct *queue, struct work_struct *work);
  int queue_delayed_work(struct workqueue_struct *queue, struct work_struct
  *work, unsigned long delay);

  在queue_delay_work()中指定 delay,是为了保证至少在经过一段给定的最小延迟时间以后,工作队列中的任务才可以真正执行。

  工作队列中的任务由相关的工作线程执行,可能是在一个无法预期的时间(取决于负载,中断等等),或者是在一段延迟以后。任何一个在工作队列中等待了无限长的时间也没有运行的任务可以用下面的方法取消:

  int cancel_delayed_work(struct work_struct *work);
  如果当一个取消操作的调用返回时,任务正在执行中,那么这个任务将继续执行下去,但不会再加入到队列中。清空工作队列中的所有任务使用:

  void flush_workqueue(struct workqueue_struct *queue);
  销毁工作队列使用:

  void destroy_workqueue(struct workqueue_struct *queue);
  不是所有的驱动程序都必须有自己的工作队列。驱动程序可以使用内核提供的缺省工作队列。由于这个工作队列由很多驱动程序共享,任务可能会需要比较长一段时间才能开始执行。为了解决这一问题,工作函数中的延迟应该保持最小或者干脆不要。

  需要特别注意的是缺省队列对所有驱动程序来说都是可用的,但是只有经过GP许可的驱动程序可以用自定义的工作队列:

  int schedule_work(struct work_struct *work); -- 向工作队列中添加一个任务
  int schedule_delayed_work(struct work_struct *work, unsigned long delay);
-- 向工作队列中添加一个任务并延迟执行

  当模块被缷载时应该去调用一个 flash_scheduled_work() 函数,这个函数会使等待队列中所有的任务都被执行。

工作队列
在Linux内核中,对下半部(或者说推后执行的工作)的处理方式有好几种,包括BH(bottom 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/n(n代表CPU的序号,从0开始);再者就是自己创建一个专用的工作者线程。对于通常情况下,驱动开发者是不必关心工作者线程的,缺省的工作者线程能够做的很好。如果缺省的队列不能满足要求,自己创建一个工作者线程也很简单,只需要调用:
struct workqueue_struct *create_workqueue(const char *name);
调度时使用如下函数:
int queue_work(struct workqueue_struct *wq, struct work_struct *work); 

int queue_delayed_work(struct workqueue_struct *wq, struct work_struct *work, unsigned long delay);
它们与schedule_work()以及schedule_delayed_work()是类似的。

阅读(1589) | 评论(0) | 转发(1) |
0

上一篇:等待队列学习笔记

下一篇:dev_dbg

给主人留下些什么吧!~~