工作队列分析

转载 2012年03月29日 20:15:53

http://linux.chinaunix.net/techdoc/net/2007/01/08/947611.shtml

一、用法
struct cpu_workqueue_struct {
spinlock_t lock;
long remove_sequence; /* Least-recently added (next to run) */
long insert_sequence; /* Next to add */
struct list_head worklist;
wait_queue_head_t more_work;
wait_queue_head_t work_done;
struct workqueue_struct *wq;
struct task_struct *thread;
int run_depth;  /* Detect run_workqueue() recursion depth */
} ____cacheline_aligned;
/*
* The externally visible workqueue abstraction is an array of
* per-CPU workqueues:
*/
struct workqueue_struct {
struct cpu_workqueue_struct *cpu_wq;
const char *name;
struct list_head list;  /* Empty if single thread */
};
工作队列的使用很简单。
1.首先是建立一个工作队列:
struct workqueue_struct *keventd_wq;
keventd_wq = create_workqueue("events");
2.然后就是在这个队列中insert你所要做的“工作”:
DECLARE_WORK(work, func, data) 
queue_work(keventd_wq, work);
struct work_struct {
unsigned long pending;
struct list_head entry;
void (*func)(void *);
void *data;
void *wq_data;
struct timer_list timer;
};
初始化有两种方法。
一种为静态方法:
#define __WORK_INITIALIZER(n, f, d) {    \
        .entry = { &(n).entry, &(n).entry },   \
.func = (f),      \
.data = (d),      \
.timer = TIMER_INITIALIZER(NULL, 0, 0),   \
}
#define DECLARE_WORK(n, f, d)     \
struct work_struct n = __WORK_INITIALIZER(n, f, d)
另一种为动态方法:
/*
* initialize all of a work-struct:
*/
#define INIT_WORK(_work, _func, _data)    \
do {       \
  INIT_LIST_HEAD(&(_work)->entry);  \
  (_work)->pending = 0;    \
  PREPARE_WORK((_work), (_func), (_data)); \
  init_timer(&(_work)->timer);   \
} while (0)
二、执行过程

create_workqueue() -> __create_workqueue()
struct workqueue_struct *__create_workqueue(const char *name,
         int singlethread)
{
int cpu, destroy = 0;
struct workqueue_struct *wq;
struct task_struct *p;
wq = kzalloc(sizeof(*wq), GFP_KERNEL);
//为每个CPU建立一个结构
wq->cpu_wq = alloc_percpu(struct cpu_workqueue_struct);
...
wq->name = name;
mutex_lock(&workqueue_mutex);
if (singlethread) {
  ...
} else {
  list_add(&wq->list, &workqueues);
  for_each_online_cpu(cpu) {
   //为每个CPU创建一个线程
   p = create_workqueue_thread(wq, cpu);
   if (p) {
    kthread_bind(p, cpu);
    //唤醒这个线程执行工作
    wake_up_process(p);
   } else
    destroy = 1;
  }
}
mutex_unlock(&workqueue_mutex);
...
return wq;
}
create_workqueue() -> __create_workqueue() -> create_workqueue_thread()
static struct task_struct *create_workqueue_thread(struct workqueue_struct *wq,
         int cpu)
{
struct cpu_workqueue_struct *cwq = per_cpu_ptr(wq->cpu_wq, cpu);
struct task_struct *p;
spin_lock_init(&cwq->lock);
cwq->wq = wq;
cwq->thread = NULL;
cwq->insert_sequence = 0;
cwq->remove_sequence = 0;
INIT_LIST_HEAD(&cwq->worklist);
init_waitqueue_head(&cwq->more_work);
init_waitqueue_head(&cwq->work_done);
if (is_single_threaded(wq))
  p = kthread_create(worker_thread, cwq, "%s", wq->name);
else
  //创建一个线程,这个线程以cwq为数据执行worker_thread这个函数
  p = kthread_create(worker_thread, cwq, "%s/%d", wq->name, cpu);
if (IS_ERR(p))
  return NULL;
cwq->thread = p;  //
return p;
}
create_workqueue() -> __create_workqueue() -> create_workqueue_thread() -> worker_thread()
//本函数在一个死循环等待工作的到来,这一般在睡眠状态中,等待被唤醒执行工作
//当有工作到来时queue_work()会将这个线程唤醒
static int worker_thread(void *__cwq)
{
struct cpu_workqueue_struct *cwq = __cwq;
DECLARE_WAITQUEUE(wait, current);
...
current->flags |= PF_NOFREEZE;
//设置优先级
set_user_nice(current, -5);
...
set_current_state(TASK_INTERRUPTIBLE);
while (!kthread_should_stop()) {
  //将本线程加入睡眠队列,用于睡眠后可以被唤醒
  add_wait_queue(&cwq->more_work, &wait);
  //如果没用被执行的“工作”,则将自己切换出去,进入睡眠状态
  if (list_empty(&cwq->worklist))
   schedule();
  else //否则或是被唤醒
   __set_current_state(TASK_RUNNING);
  remove_wait_queue(&cwq->more_work, &wait);
  
  //工作队列非空,执行工作
  if (!list_empty(&cwq->worklist))
   run_workqueue(cwq);
  set_current_state(TASK_INTERRUPTIBLE);
}
__set_current_state(TASK_RUNNING);
return 0;
}
create_workqueue() -> __create_workqueue() -> create_workqueue_thread() -> worker_thread() 
-> run_workqueue()
//该函数执行真正的工作
static void run_workqueue(struct cpu_workqueue_struct *cwq)
{
unsigned long flags;
spin_lock_irqsave(&cwq->lock, flags);
...
//顺次执行队列中的所有工作
while (!list_empty(&cwq->worklist)) {
  struct work_struct *work = list_entry(cwq->worklist.next,
      struct work_struct, entry);
  void (*f) (void *) = work->func;
  void *data = work->data;
  //从队列中删除待执行的任务
  list_del_init(cwq->worklist.next);
  spin_unlock_irqrestore(&cwq->lock, flags);
  BUG_ON(work->wq_data != cwq);
  clear_bit(0, &work->pending);
  //执行“工作”
  f(data);
  spin_lock_irqsave(&cwq->lock, flags);
  cwq->remove_sequence++;
  wake_up(&cwq->work_done);  //
}
cwq->run_depth--;
spin_unlock_irqrestore(&cwq->lock, flags);
}

三、工作线程创建的详细过程

create_workqueue() -> __create_workqueue() -> create_workqueue_thread() -> kthread_create()
struct task_struct *kthread_create(int (*threadfn)(void *data),
       void *data,
       const char namefmt[],
       ...)
{
//初始化用于创建线程的辅助结构
struct kthread_create_info create;
DECLARE_WORK(work, keventd_create_kthread, &create);
create.threadfn = threadfn;
create.data = data;
init_completion(&create.started);
init_completion(&create.done);
if (!helper_wq) //首先创建辅助工作队列
  work.func(work.data);
else {
  //注意,“创建一个工作队列”这个工作本身又是属于helper_wq工作队列
  //的一项工作,所以,将这个工作加入的辅助工作队列中等待执行。
  queue_work(helper_wq, &work);
  wait_for_completion(&create.done);
}
...
return create.result;
}
create_workqueue() -> __create_workqueue() -> create_workqueue_thread() -> kthread_create()
-> keventd_create_kthread()
//最终会调用kernel_thread为每个工作队列创建一个线程
//这样,被创建的线程会以create为数据执行kthread(如下),而kthread中则执行create中的threadfn(data),
//即为create_workqueue_thread中的worker_thread(cwq),即为我们工作队列要执行的函数了。
static void keventd_create_kthread(void *_create)
{
struct kthread_create_info *create = _create;
int pid;
/* We want our own signal handler (we take no signals by default). */
pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD);
if (pid result = ERR_PTR(pid);
} else {
  wait_for_completion(&create->started);
  read_lock(&tasklist_lock);
  create->result = find_task_by_pid(pid);
  read_unlock(&tasklist_lock);
}
complete(&create->done);
}
static int kthread(void *_create)
{
struct kthread_create_info *create = _create;
int (*threadfn)(void *data);
void *data;
...
threadfn = create->threadfn;
data = create->data;
...
if (!kthread_should_stop())
  ret = threadfn(data);
...
return 0;
}
四、插入“工作”
/* Preempt must be disabled. */
static void __queue_work(struct cpu_workqueue_struct *cwq,
    struct work_struct *work)
{
unsigned long flags;
spin_lock_irqsave(&cwq->lock, flags);
work->wq_data = cwq;
//将当前工作插入到工作队列中待待执行
list_add_tail(&work->entry, &cwq->worklist);
cwq->insert_sequence++;
wake_up(&cwq->more_work);  //唤醒相应线程
spin_unlock_irqrestore(&cwq->lock, flags);
}
int fastcall queue_work(struct workqueue_struct *wq, struct work_struct *work)
{
int ret = 0, cpu = get_cpu();
//如里当前工作未在队列中才插入
if (!test_and_set_bit(0, &work->pending)) {
  ...
  __queue_work(per_cpu_ptr(wq->cpu_wq, cpu), work);
  ret = 1;
}
put_cpu();
return ret;
}


相关文章推荐

工作队列分析 queue_work

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

Linux工作队列workqueue实现分析

本文档的Copyleft归rosetta所有,使用GPL发布,可以自由拷贝、转载,转载时请保持文档的完整性。 参考资料:《Linux内核设计与实现》第3版 LKD3e、linux-2.6.27 ...
  • rosetta
  • rosetta
  • 2012年06月11日 14:58
  • 3678

linux中断处理下文: 工作队列分析

工作队列(work queue)是另外一种将工作推后执行的形式,它和前面讨论的tasklet有所不同。工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。这样...

Linux工作队列workqueue实现分析

本文档的Copyleft归rosetta所有,使用GPL发布,可以自由拷贝、转载,转载时请保持文档的完整性。 参考资料:《Linux内核设计与实现》第3版 LKD3e、linux-2.6.27 ...

linux内核分析之工作队列

可延迟函数和工作队列非常相似,但是他们的区别还是很大的。主要区别在于:可延迟函数运行在中断上下文中,而工作队列中的函数运行在进程上下文中。在中断上下文中不可能发生进程切换。可延迟函数和工作队列中的函数...
  • bullbat
  • bullbat
  • 2011年12月25日 16:27
  • 1609

工作队列代码分析

1.   首先应该了解一下表示工作队列的结构体workqueue_struct,      该结构体定义在文件kernel/workqueue.c中。定义如下:       struct ...

rabbitmq 工作队列 java 实现

  • 2016年08月23日 17:33
  • 11KB
  • 下载

中断服务下半部之工作队列详解

  • 2009年05月01日 09:40
  • 14KB
  • 下载

Linux中的工作队列

工作队列(work queue)是Linux kernel中将工作推后执行的一种机制。这种机制和BH或Tasklets不同之处在于工作队列是把推后的工作交由一个内核线程去执行,因此工作队列的优势就在于...

RabbitMQ的工作队列和路由.doc

  • 2017年06月27日 14:28
  • 58KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:工作队列分析
举报原因:
原因补充:

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