工作队列是一种不同于软中断和微线程的一种下半部延迟机制。工作队列将工作延迟到一个内核线程中执行,它运行在进程上下文中,它是可调度的,并且可以休眠。通常,如果延迟的工作中需要休眠,就使用工作队列,否则使用软中断或微线程。由于内核开发者反对创建一个新的内核线程,因此,应当尽量使用工作队列,它其实是事先创建了一个内核线程。
工作队列的实现
工作队列实际上是一种创建内核线程以处理从其他地方入队的任务的接口。这些内核线程称为工作者线程。你可以创建一个特殊的工作者线程来处理延迟工作,然而,工作队列为我们提供了一个默认的工作者线程。在大多数情况下,直接使用该默认工作者线程就可以了。默认的工作者线程称为events/n,其中n为处理器的编号。
代表线程的数据结构
struct workqueue_struct { |
struct cpu_workqueue_struct cpu_wq[NR_CPUS]; |
struct list_head list; |
const char *name; |
int singlethread; |
int freezeable; |
int rt; |
}; |
每个处理器对应一个struct cpu_workqueue_struct的数据结构。
struct cpu_workqueue_struct { |
| ||
spinlock_t lock; |
/*lock protecting this structure */ | ||
struct list_head worklist; |
/*list of work */ | ||
wait_queue_head_t more_work; |
| ||
|
struct work_struct *current_struct; | ||
|
struct workqueue_struct *wq; /* associated workqueue_struct */ | ||
|
task_t *thread; |
/*associated thread */ | |
|
|
代表工作的数据结构
struct work_struct { |
atomic_long_t data; |
struct list_head entry; |
work_func_t func; |
}; |
工作者线程的核心代码如下:
for(;;) { |
prepare_to_wait(&cwq->more_work,&wait, TASK_INTERRUPTIBLE); |
if (list_empty(&cwq->worklist)) |
schedule(); |
finish_wait(&cwq->more_work,&wait); |
run_workqueue(cwq); |
} |
在函数run_workqueue(),执行实际的延迟工作:
while(!list_empty(&cwq->worklist)) { |
struct work_struct *work; |
work_func_t f; |
void*data; |
work= list_entry(cwq->worklist.next, struct work_struct, entry); |
f= work->func; |
list_del_init(cwq->worklist.next); |
work_clear_pending(work); |
f(work); |
} |
工作队列相关数据结构的关系

最上层的工作者线程,可能有多个类型。每个处理器上都有每一种类型的工作者线程。内核代码可以根据需要创建工作者线程。默认情况下,工作者线程是events。每个工作者线程由结构cpu_workqueue_struct来表示。结构workqueue_struct代表每个类型的所有工作者线程。例如,假设除了默认的events类型的工作者线程外,还创建了一个falcon类型的工作者线程。假设计算机有4个处理器,那么有4个events线程(因而,有4个cpu_workqueue_struct结构)和4个falcon线程(因而,有另外4个cpu_workqueue_struct结构)。有2个workqueue_struct,分别对应events类型和falcon类型。
使用默认的工作队列
创建工作队列
静态方式:DECLARE_WORK(name,void (*func)(void *), void *data);
动态方式:INIT_WORK(struct work_struct *work, void (*func)(void *), void *data);
工作队列处理函数:void work_handler(void *data)
调度工作队列:schedule_work(&work)或schedule_delayed_work(&work,delay);
Flush工作队列
void flush_scheduled_work(void);
该函数不能取消任何延迟的工作,即被schedule_delayed_work()调度的工作。为了取消一个延迟的工作,调用:int cancel_delayed_work(struct work_struct *work);
创建一个新的工作队列
struct workqueue_struct *create_workqueue(const char *name);
name为工作队列的名称,如默认的工作队列名称为events,如下所示:
struct workqueue_struct *keventd_wq;
keventd_wq= create_workqueue(“events”);
它将为每个处理器创建一个工作者线程,并使之处于就绪状态。
调度工作队列
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)
flush一个工作队列
flush_workqueue(struct workqueue_struct *wq)
总结如下:
内核中提供了两个辅助接口来使用工作队列:
workqueue_struct和work_struct。
使用步骤如下:
-
创建与一个或多个内核线程关联的工作队列(或一个workqueue_struct结构体)。为了创建一个服务于某个工作队列的内核线程,使用create_singlethread_workqueue()。创建系统中的一个每-CPU工作者线程,使用create_workqueue()。内核也提供了默认的每-CPU工作者线程供你直接使用(event/n,其中n是CPU号)。
-
创建一个工作单元(或一个work_struct变量)。一个work_struct变量使用INIT_WORK()进行初始化。
-
提交工作单元到工作队列中。使用queue_work()将一个工作单元提交到一个专门的工作队列中。使用schedule_work()将一个工作单元提交给默认的内核工作者线程。
工作队列使用模板
UsingWorkqueue to Offload Work from Interrupt Handlers |
struct roller_device_struct{ /*… */ struct work_struct wklt; /*… */ };
void __init roller_init() { struct roller_device_struct *dev_struct; /*… */ /*Initialize tasklet */ INIT_WORK(&dev_struct-> wklt, roller_analyze, dev); }
/*The bottom half */ void roller_analyze() { /*… */ }
/*The interrupt handler */ static irqreturn_t roller_interrupt(int irq, void *dev_id) { /*… */ /*Mark workqueue as pending */ schedule_work(&dev_struct->wklt); return IRQ_HANDLED; } |