工作队列
工作队列在linux2.6版本中被引入,类似于linux2.4中的“task queue”(任务队列)。工作队列机制允许内核函数延迟执行,也就是你将一个即将要执行的函数放进工作队列中,工作队列机制中的工作线程会在一定时间之后执行该函数。
虽然有点像可延迟函数,但是他们还是有一些区别。最主要的区别在于可延迟函数在中断上下文中运行,而工作队列中的函数在进程上下文中执行。在进程上下文中运行是可以执行阻塞函数(例如,需要访问磁盘上数据块的函数)。可延迟函数和工作队列中的函数都不能访问进程的用户模式地址空间。
#工作队列的数据结构
基于linux2.6.0源码
与工作队列相关的主要数据结构是workqueue_struct描述符,里面包含元素个数为NR_CPUS的cpu_workqueue_struct数组
struct workqueue_struct {
struct cpu_workqueue_struct cpu_wq[NR_CPUS];
};
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; /* 指向包含此描述符的 workqueue_struct 结构的指针 */
task_t *thread; /* 结构体的工作线程的进程描述符指针 */
struct completion exit; /* run_workqueue() 的当前执行深度
(当工作队列列表中的函数阻塞时,该字段可能会大于1 */
} ____cacheline_aligned;
每个待执行的函数都会挂在双向链表worklist中,每个待执行的函数都由结构work_struct描述符来描述
struct work_struct {
unsigned long pending; /* 如果函数已经在工作队列列表中,则设置为 1,否则设置为 0 */
struct list_head entry; /* 指向待处理函数列表中下一个和上一个元素的指针 */
void (*func)(void *); /* 要执行函数的地址 */
void *data; /* 作为参数传递给挂起函数的指针 */
void *wq_data; /* 通常指向父 cpu_workqueue_struct 描述符 */
struct timer_list timer; /* 用于延迟挂起函数执行的软件定时器 */
};
工作队列的函数
**create_workqueue(“foo”) **函数接收一个字符串作为其参数,并返回新创建的工作队列的 workqueue_struct 描述符的地址。该函数还创建了 n 个工作线程(其中 n 是系统中有效存在的 CPU 数量),以传递给该函数的字符串命名:foo/0、foo/1 等。**create_singlethread_workqueue() 函数功能类似,但它只创建一个工作线程,不管系统中的 CPU 数量是多少。为了销毁一个工作队列,内核调用destroy_workqueue()**函数,它接收一个指向workqueue_struct数组的指针作为参数。
**queue_work() **向工作队列中插入一个函数(已经打包在 work_struct 描述符中);它接收一个指向 workqueue_struct 描述符的指针 wq 和一个指向 work_struct 描述符的指针 work。 queue_work() 主要执行以下步骤:
- 检查要插入的函数是否已经存在于工作队列中(work->pending 字段等于 1);如果是,则终止.
- 将 work_struct 描述符添加到工作队列列表,并将 work->pending 设置为 1。
- 如果工作线程在本地 CPU 的 cpu_workqueue_struct 描述符的 more_work 等待队列中休眠,则该函数将其唤醒。
int queue_work(struct workqueue_struct *wq, struct work_struct *work)
{
unsigned long flags;
int ret = 0, cpu = get_cpu();
struct cpu_workqueue_struct *cwq = wq->cpu_wq + cpu;
if (!test_and_set_bit(0,