linux设备驱动下的工作队列(workqueue)

原创 2012年03月29日 18:50:19

       从表面和使用来看,工作队列类似与tasklet,它们都允许内核代码请求某个函数在将来的时间被调用。

但实际上它们有一些非常重要的区别:

1、  tasklet在软件中断上下文中运行,因此所有的tasklet代码都必须是原子的。而工作队列函数在一个特殊内核进程的上下文总运行,所以具有更好的灵活性。更主要的是工作队列函数可以休眠。

2、  tasklet始终运行在被初始提交的同一处理器上,而工作队列默认是这样。

3、  内核代码可以请求工作队列函数的执行延迟给定的时间间隔。有一种提交工作队列的函数接口是:

int queue_delayed_work(struct workqueue_struct *wq,

                    struct delayed_work *dwork, unsigned long delay)

queue_delayed_work - queue work on a workqueue after delay

 

二者的关键区别在于:tasklet会在很短的时间段内很快执行,并且以原子模式执行,而工作队列函数可具有更长的延迟且不必原子化。

工作队列的初始化:

#define __INIT_WORK(_work, _func, _onstack)				\
	do {								\
		__init_work((_work), _onstack);				\
		(_work)->data = (atomic_long_t) WORK_DATA_INIT();	\
		INIT_LIST_HEAD(&(_work)->entry);			\
		PREPARE_WORK((_work), (_func));				\
	} while (0)
#endif

#define INIT_WORK(_work, _func)					\
	do {							\
		__INIT_WORK((_work), (_func), 0);		\
	} while (0)


工作队列也是用内核中的list_head双向链表;

 

 

工作队列的提交:

值得注意的是:在一般情况下,我们都会用schedule_work函数来提交工作队列,这里

int schedule_work(struct work_struct *work)
{
return queue_work(keventd_wq, work);
}

该函数是将work_struct加入一个全局的keventd_wq队列中,也就是内核提供的共享的默认工作队列。

如果我们有特殊的延时需求,那我们只有建立自己的专用工作队列。

使用create_workqueue系列宏定义。

然后用下面的函数提交工作队列:

int queue_work(struct workqueue_struct *wq, struct work_struct *work)

int queue_delayed_work(struct workqueue_struct *wq,

                            struct delayed_work *dwork, unsigned long delay)

其中queue_delayed_work就用到了linux的动态定时器,所以才有delay的效果。

我们主要看queue_work函数:

int schedule_work(struct work_struct *work)

{

         return queue_work(keventd_wq, work);

}

 

int queue_work(struct workqueue_struct *wq, struct work_struct *work)

{

         int ret;

 

         ret = queue_work_on(get_cpu(), wq, work);

         put_cpu();

 

         return ret;

}

 

int

queue_work_on(int cpu, struct workqueue_struct *wq, struct work_struct *work)

{

         int ret = 0;

 

         if (!test_and_set_bit(WORK_STRUCT_PENDING, work_data_bits(work))) {

                   BUG_ON(!list_empty(&work->entry));

                   __queue_work(wq_per_cpu(wq, cpu), work);

                   ret = 1;

         }

         return ret;

}

 

static void __queue_work(struct cpu_workqueue_struct *cwq,

                             struct work_struct *work)

{

         unsigned long flags;

 

         debug_work_activate(work);

         spin_lock_irqsave(&cwq->lock, flags);

         insert_work(cwq, work, &cwq->worklist);

         spin_unlock_irqrestore(&cwq->lock, flags);

}

 

static void insert_work(struct cpu_workqueue_struct *cwq,

                            struct work_struct *work, struct list_head *head)

{

         trace_workqueue_insertion(cwq->thread, work);

 

         set_wq_data(work, cwq);

         /*

          * Ensure that we get the right work->data if we see the

          * result of list_add() below, see try_to_grab_pending().

          */

         smp_wmb();

         list_add_tail(&work->entry, head);

         wake_up(&cwq->more_work);

}


 

最终还是list_add_tail(&work->entry, head); 双向链表随处可见

 

 

工作队列的执行:

tasklet是利用软中断不同,工作队列的执行是在一个特殊的内核进程中运行的。

worker_threadà run_workqueue

worker_thread是怎么来的,暂时不过问了,就当他是一个特殊的内核进程。

 

run_workqueue的内容很明了:遍历链表,执行已提交的工作队列的注册回调函数。

 

从形式上看,workqueuetasklet很类似,在中断顶半部和中断底半部中的使用中,他们的使用方法也是类似的。

 

 

相关文章推荐

Linux workqueue工作原理

1. 什么是workqueue        Linux中的Workqueue机制就是为了简化内核线程的创建。通过调用workqueue的接口就能创建内核线程。并且可以根据当前系统CPU的个数创建线...
  • MyArrow
  • MyArrow
  • 2012年10月19日 17:02
  • 28953

linux工作队列 - workqueue总览

workqueue归入中断子系统是由于和中断处理有密切关系,写博客重要在于整理自己的思绪,写的时候会把一些不懂的细节问题暴露出来,这样会把问题看的更透彻,workqueue的代码在文件kernel/w...

workqueue

Workqueue引入 在之前的softirq中提到过,内核在中断的bottom half引入了softirq,  tasklet,  workqueue。 而softirq和tasklet只能用在中...

linux内核中断处理的工作队列workqueue机制

linux内核中断处理的工作队列workqueue机制 工作队列(workqueue)是另外一种将工作 推后执行的形式,它和我们前面讨论的小任务有所不同。工作队列可以把工作推后,交由一个内核线程去执行...
  • yihui8
  • yihui8
  • 2010年03月31日 16:43
  • 1153

工作队列分析 queue_work

一、用法  struct cpu_workqueue_struct {  spinlock_t lock;  long remove_sequence; /* Least-recentl...

linux设备驱动之工作队列学习(workqueue)

首先要注意本文的两个概念:(1)使用内核提供的工作队列, (2)自己创建工作队列 http://blog.csdn.net/fontlose/article/details/8286445   工作队...

C语言访问MCU寄存器的三种方式

C语言访问MCU寄存器的三种方式 MCU中的特殊功能寄存器SFR,实际上就是SRAM地址已经确定的SRAM单元,在C语言环境下对其访问归纳起来有3种方法。...

关于PingPong缓冲的困惑

请问在EDMA3里的PingPong缓冲机制中,ping缓冲区和pong缓冲区时存在于哪里?是通道控制器还是每一个通道都分为ping缓冲区和pong缓冲区?另外,在进行pingpong传输的时候用的是...

Linux设备驱动四 (4)中断下半部分工作队列

1、工作队列的使用 按惯例,在介绍工作队列如何实现之前,先说说如何使用工作队列实现下半部。 步骤一、定义并初始化工作队列: 创建工作队列函数: ...

linux设备驱动归纳总结(六):3.中断下半部之工作队列

linux设备驱动归纳总结(六):3.中断的上半部和下半部——工作队列 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:linux设备驱动下的工作队列(workqueue)
举报原因:
原因补充:

(最多只允许输入30个字)