分享

内核工作队列(workqueue)

 clover_xian 2013-06-27

内核工作队列(workqueue)

本文基于2.6.32的内核,此时的工作队列还不是cmwq

为什么使用workqueue
在内核代码中,经常希望延缓部分工作到将来某个时间执行,这样做的原因很多,比如:
1、在持有锁时做大量(或者说费时的)工作不合适。
2、希望将工作聚集以获取批处理的性能。
3、调用了一个可能导致睡眠的函数使得在此时执行新调度非常不合适。
4、工作队列的优点:(1)使用简单;(2)执行在进程上下文,从而可以睡眠被调度和抢占;(3)在多核环境下使用也非常友好。

内核中提供了许多机制来提供延迟执行,使用最多则是workqueue:
1、如中断的下半部处理可延迟中断上下文中的部分工作;
2、定时器可指定延迟一定时间后执行某工作;
3、workqueue 则允许在进程上下文环境下延迟执行;
4、内核中曾短暂出现过的慢工作机制(slow work mechanism);
5、异步函数调用(asynchronous function calls);
6、各种私有实现的线程池;

术语
workqueue:所有工作项(需要被执行的工作)被排列于该队列。
worker thread:是一个用于执行workqueue中各个工作项的内核线程,当workqueue中没有工作项时,该线程将变为idle状态。
single threaded(ST):worker thread的表现形式之一,在系统范围内,只有一个worker thread为workqueue服务。
multi threaded(MT):worker thread的表现形式之一,在多CPU系统上每个CPU上都有一个worker thread为workqueue服务。

使用步骤
1、创建workqueue(如果使用内核默认的workqueue,此步骤略过);
2、创建工作项work_struct;
3、向workqueue提交工作项;

工作项

struct work_struct {
    atomic_long_t data;
    struct list_head entry;
    work_func_t func;
    struct lockdep_map lockdep_map;
};
struct delayed_work {
    struct work_struct work;
    struct timer_list timer;
};

1、静态地创建工作项:
DECLARE_WORK(n, f)
DECLARE_DELAYED_WORK(n, f)
2、动态地创建工作项:
INIT_WORK(struct work_struct work, work_func_t func);
PREPARE_WORK(struct work_struct work, work_func_t func);
INIT_DELAYED_WORK(struct delayed_work work, work_func_t func);
PREPARE_DELAYED_WORK(struct delayed_work work, work_func_t func);

内核默认的workqueue

// 定义
static struct workqueue_struct *keventd_wq __read_mostly;

// 初始化
...
keventd_wq = create_workqueue("events"); // MT worker thread 模式.
...

// 确认对应的内核线程数目, 应等于 CPU 核数
$ lscpu
Architecture:          x86_64
CPU(s):                3
Thread(s) per core:    1
$ ps aux | grep "events"
root         9  0.0  0.0      0     0 ?        S    Feb09   1:15 [events/0]
root        10  0.0  0.0      0     0 ?        S    Feb09   0:59 [events/1]
root        11  0.0  0.0      0     0 ?        S    Feb09   0:59 [events/2]

// 工作项加入 keventd_wq 由下面两个函数之一完成:
int schedule_work(struct work_struct *work)
{
    return queue_work(keventd_wq, work);
}
int schedule_delayed_work(struct delayed_work *dwork, unsigned long delay)
{
    return queue_delayed_work(keventd_wq, dwork, delay);
}

用户自定义的workqueue
1、创建workqueue:
create_singlethread_workqueue(name) // 仅对应一个内核线程
create_workqueue(name) // 对应多个内核线程, 同上文.
2、向workqueue中提交工作项:
int queue_work(workqueue_t queue, work_t work);
int queue_delayed_work(workqueue_t queue, work_t work, unsigned long delay);

example

12345678910111213141516171819202122232425262728293031323334353637
#include <linux/module.h>
#include <linux/init.h>
#include <linux/workqueue.h>
 
static struct workqueue_struct *queue = NULL;
static struct work_struct work;
 
static void work_handler(struct work_struct *data)
{
printk(KERN_ALERT "work handler for work_item in queue helloworkqueue\n");
// workqueue 中的每个工作完成之后就被移除 workqueue.
// 下面的语句会造成"死机", 原因可能是该 workqueue 占据了所有的 CPU 时间.
// queue_work(queue, &work);
}
 
static int __init test_init(void)
{
queue = create_singlethread_workqueue("helloworkqueue");
if (!queue)
{
goto err;
}
INIT_WORK(&work, work_handler);
queue_work(queue, &work);
 
return 0;
err:
return -1;
}
static void __exit test_exit(void)
{
destroy_workqueue(queue);
}
 
MODULE_LICENSE("GPL");
module_init(test_init);
module_exit(test_exit);

资料
IBM developworks Linux 的并发可管理工作队列机制探讨

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多