workqueue与tasklet功能相似,都是让某个函数在将来被调用。但是两者件也有一些区别:
工作队列(workqueue)是一种将工作推后执行的形式。工作队列可以把工作推后,交由一个内核线程去执行。
数据结构:
把推后执行的任务叫做工作(work),描述它的数据结构为work_struct:
struct work_struct {
atomic_long_t data; /*工作处理函数func的参数*/
#define WORK_STRUCT_PENDING 0 /* T if work item pending execution */
#define WORK_STRUCT_STATIC 1 /* static initializer (debugobjects) */
#define WORK_STRUCT_FLAG_MASK (3UL)
#define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK)
struct list_head entry; /*连接工作的指针*/
work_func_t func; /*工作处理函数*/
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
把工作(work)连接成队列称为工作队列(workqueue),其数据结构为workqueue_struct:
头文件
struct workqueue_struct {
struct cpu_workqueue_struct *cpu_wq;
struct list_head list;
const char *name; /*workqueue name*/
int singlethread; /*是不是单线程 - 单线程我们首选第一个CPU -0表示采用默认的工作者线程event*/
int freezeable; /* Freeze threads during suspend */
int rt;
};
1. 创建工作队列
struct workqueue_struct *create_workqueue(const char *name) //多处理下 创建多个内核线程
struct workqueue_struct *create_singlethread_workqueue(const char *name) // 仅对应一个内核线程
函数传参是内核中工作队列的名称,返回值是workqueue_struct结构体的指针,该结构体用来维护一个等待队列。
例如:
struct workqueue_struct * test_wq; //定义工作队列
test_wq = create_workqueue(" testwq");
2. 创建工作(work)
DECLARE_WORK(n, f);
DECLARE_DELAYED_WORK(n, f);
头文件#include
#define DECLARE_WORK(n, f) \
struct work_struct n = __WORK_INITIALIZER(n, f)
定义并初始化一个叫 n 的work_struct
数据结构,它对应的的处理函数是f。
/*初始化之前需要定义一个work_struct 结构*/
INIT_WORK(struct work_struct work, work_func_t func); /*初始化work_struct 结构*/
PREPARE_WORK(struct work_struct work, work_func_t func); /*一般用于更新work_struct 结构*/
INIT_DELAYED_WORK(struct delayed_work work, work_func_t func); /*初始化一个延时任务*/
PREPARE_DELAYED_WORK(struct delayed_work work, work_func_t func);
/*linux/workqueue.h*/
#define INIT_WORK(_work, _func) \
do { \
(_work)->data = (atomic_long_t) WORK_DATA_INIT(); \
INIT_LIST_HEAD(&(_work)->entry); \
PREPARE_WORK((_work), (_func)); \
} while (0)
#endif
3. 将work添加到queue
工作队列和worl结构体都已经实现了,接下来就可以调度了,使用一下函数:
/*kernel/workqueue.c*/
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);
将指定的任务(work_struct),添加到指定的工作队列中。
调度并不代表处理函数能够马上执行,这由内核进程调度决定。
4. 注销工作队列
刷新等待队列函数:
/*kernel/workqueue.c*/
void flush_workqueue(struct workqueue_struct *wq)
该函数会一直等待,知道指定的等待队列中所有的任务都执行完毕并从等待队列中移除。
注销等待队列:
/*kernel/workqueue.c*/
void destroy_workqueue(struct workqueue_struct *wq)
该函数是是创建等待队列的反操作,注销掉指定的等待队列。
普通任务示例:
struct workqueue_struct *wq;
struct work_struct work;
wq = create_workqueue("workqueue");
INIT_WORK(&work, func);
queue_work(wq, &work);
destroy_workqueue(wq);
延迟任务示例:
struct workqueue_struct *wq;
struct delayed_work work;
wq = create_workqueue("workqueue");
INIT_DELAYED_WORK(&work, func);
queue_delayed_work(wq, &work, timeout * HZ);
cancel_delayed_work(&work);
destroy_workqueue(wq);
工作队列应用示例:
#include
#include
#include
#include
#define DEBUG_SWITCH 1
#if DEBUG_SWITCH
#define P_DEBUG(fmt, args...) printk("<1>" "[%s]" fmt, __FUNCTI ON__, ##args)
#else
#define P_DEBUG(fmt, args...) printk("<7>" "[%s]" fmt, __FUNCTI ON__, ##args)
#endif
struct workqueue_struct *ZP1015_wq; //1.定义工作队列
struct work_struct ZP1015_work; //2定义work结构体
void ZP1015_func(struct work_struct *work) //2实现处理函数
{
printk("hello ZP1015!\n");
}
irqreturn_t irq_handler(int irqno, void *dev_id)
{
printk("key down\n");
queue_work(ZP1015_wq ,&ZP1015_work); //3调度任务
return IRQ_HANDLED;
}
static int __init test_init(void) //模块初始化函数
{
int ret;
/*work*/
ZP1015_wq = create_workqueue("ZP1015"); //1初始化工作对列
INIT_WORK(&ZP1015_work, ZP1015_func); //2初始化work结构体
ret = request_irq(IRQ_EINT1, irq_handler,
IRQF_TRIGGER_FALLING, "key INT_EINT1", NULL);
if(ret){
P_DEBUG("request irq failed!\n");
return ret;
}
printk("hello irq\n");
return 0;
}
static void __exit test_exit(void) //模块卸载函数
{
flush_workqueue(ZP1015_wq); //4刷新工作队列
destroy_workqueue(ZP1015_wq); //4注销工作队列
free_irq(IRQ_EINT1, NULL);
printk("good bye irq\n");
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZP1015");
MODULE_VERSION("v0.1");
共享队列就是使用共享的工作队列,从而省去创建和注销工作队列的步骤。
当然,如果有多个不同的任务都加入到这个工作对列中,每个任务调度的速度就会比较慢,肯定不如自己创建一个爽。不过,一般默认工作队列都能满足要求,不需要创建一个新的。
共享队列使用分为两步:
1. 定义并初始化work_struct
结构体;定义 func 函数;(这里和上面的初始化完全相同)
2. 任务调度;
任务调度:
/*kernel/workqueue.c*/
int schedule_work(struct work_struct *work)
int schedule_work(struct work_struct *work)
{
return queue_work(keventd_wq, work);
}
该函数会把 work_struct
结构体加入到默认工作队列events中。
代码示例:
#include
#include
#include
#include
#define DEBUG_SWITCH 1
#if DEBUG_SWITCH
#define P_DEBUG(fmt, args...) printk("<1>" "[%s]"fmt, __FUNCTI ON__, ##args)
#else
#define P_DEBUG(fmt, args...) printk("<7>" "[%s]"fmt, __FUNCTI ON__, ##args)
#endif
struct work_struct ZP1015_work; //定义work结构体
void ZP1015_func(struct work_struct *work)
{
printk("hello Linux world!\n");
}
irqreturn_t irq_handler(int irqno, void *dev_id) //中断处理函数
{
printk("key down\n");
schedule_work(&ZP1015_work); //调度任务
return IRQ_HANDLED;
}
static int __init test_init(void) //模块初始化函数
{
int ret;
/*work*/
INIT_WORK(&ZP1015_work, ZP1015_func); //初始化work结构体
ret = request_irq(IRQ_EINT1, irq_handler,
IRQF_TRIGGER_FALLING, "key INT_EINT1", NULL);
if(ret){
P_DEBUG("request irq failed!\n");
return ret;
}
printk("hello irq\n");
return 0;
}
static void __exit test_exit(void) //模块卸载函数
{
free_irq(IRQ_EINT1, NULL);
printk("good bye irq\n");
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZP1015");
MODULE_VERSION("v0.1");