1、工作队列描述:
工作队列(work queue)是将任务推后执行的另外一种手段;它的使用方法与Tasklet非常相似,最大的不同就是工作队列的处理函数中可以使用休眠,而Tasklet的处理函数中不允许使用休眠;这是因为Tasklet工作在中断上下文,而工作队列则工作在进程上下文;
工作队列的使用又分为两种情况:
(1):利用系统中共享的工作队列来添加自己的工作;这种情况下的处理函数不能消耗太多的时间,否则就会影响共享队列中其它任务的处理;
(2):创建自己的工作队列并添加工作;
2、工作、工作队列和工作者线程:
我们把推后执行的任务叫做工作(work),使用数据结构struct work_struct或struct delayed_work来描述;这些工作以队列结构组织成工作队列(workqueue),使用数据结构struct workqueue_struct来描述;而工作者线程就是负责执行工作队列中的工作;系统默认的工作者线程是event/n,自己也可以创建自己的工作者线程;
所以,工作、工作队列、工作者线程,这三者是紧密相关的对象;
其中,struct work_struct描述的工作被调度之后,马上就会被执行;而struct delayed_work描述的工作在被调度之后,需要等待一定时间之后才能被执行;
3、利用系统中共享的工作队列来添加工作:
STEP1:声明或编写一个工作处理函数;
void xxx_do_work(struct work_struct* work);
STEP2:创建一个工作结构体变量,并把工作处理函数和参数的入口地址赋值给这个工作结构体变量;
A.编译时静态创建并初始化:
DECLARE_WORK(my_work,xxx_do_work,&data);
DECLARE_DELAYED_WORK(my_delayed_work,xxx_do_work,&data);
B.运行时动态创建并初始化:
struct work_struct my_work;
INIT_WORK(&my_work,xxx_do_work,&data);
struct delayed_work my_delayed_work;
INIT_DELAYED_WORK(&my_delayed_work,xxx_do_work,&data);
STEP3:把工作结构体变量添加到系统的共享工作队列中,以调度该工作运行;
schedule_work(&my_work);
schedule_work_on(int cpu, struct work_struct* work);
schedule_delayed_work(&my_delayed_work, jiffies);
schedule_delayed_work_on(int cpu, struct delayed_work* work, unsigned long jiffies);
执行schedule_work()之后,my_work就会马上被调度,一旦其所在的处理器上的工作者线程被唤醒,它就会被执行;添加到工作队列中的工作完成后,会自动地从工作队列中被删除;带cpu参数的表示在指定CPU上调度运行;
有时候不希望工作马上就被执行,而是希望它经过一段时间的延迟只会再执行;在这种情况下,可以调度它在指定的时间执行;可以调用schedule_delayed_work()函数来调度工作执行;
4、创建自己的工作队列来添加工作:
STEP1:声明工作处理函数和一个指向工作队列的指针;
void xxx_do_work(struct work_struct* work);
struct workqueue_struct* p_queue = NULL;
STEP2:创建自己的工作队列和工作结构体变量;
p_queue = create_workqueue("name"); //name为工作队列的名称;
p_queue = create_rt_workqueue("name");
p_queue = create_freezeable_workqueue("name");
p_queue = create_singlethread_workqueue("name");
struct work_struct my_work;
INIT_WORK(&my_work,xxx_do_work,&data);
struct delayed_work my_delayed_work;
INIT_DELAYED_WORK(&my_delayed_work,xxx_do_work,&data);
STEP3:把工作添加到自己创建的工作队列中等待执行(调度工作结构体对象执行);
int queue_work(struct workqueue_struct *wq, struct work_struct *work); //queue_work(p_queue,&my_work);
int queue_work_on(int cpu, struct workqueue_struct* wq, struct work_struct* work);
int queue_delayed_work(struct workqueue_struct* wq, struct delayed_work* work, unsigned long delay);
int queue_delayed_work_on(int cpu, struct workqueue_struct* wq, struct delayed_work* work, unsigned long delay);
STEP4:如果自己创建的工作队列不再需要时,就要删除;
void destroy_workqueue(struct workqueue_struct* wq);
5、其它函数:
void flush_workqueue(struct workqueue_struct* wq);
该函数用于等待工作队列中的所有工作都执行完之后才返回;在等待所有待处理的工作执行的时候,该函数会进入休眠状态;所以,它只能用于进程上下文中;
int flush_work(struct work_struct* work);
void flush_delayed_work(struct delayed_work* work);
这两函数用于等待指定的工作执行完成;返回之前是处于休眠状态的;
void flush_scheduled_work(void);
该函数用于刷新已经被调度的工作;
int cancel_delayed_work(struct delayed_work* work);
该函数用于取消指定的延迟工作;
例如:
下面的代码用于定义一个工作队列和一个工作队列处理函数:
struct work_struct my_wq; //定义工作队列
void my_wq_func(struct work_struct* work); //工作队列处理函数
通过如下代码初始化工作队列my_wq,并把该工作队列my_wq与一个处理函数my_wq_func绑定起来:
INIT_WORK(&my_wq, (void (*)(void*))my_wq_func, NULL);
与tasklet_schedule()对应的用于调度工作队列执行的函数为schedule_work();如:
schedule_work(&my_wq); //调度工作队列执行
使用工作队列处理中断底半部的设备驱动程序模板如下:
//定义工作队列和处理函数:
struct work_struct xxx_wq;
void xxx_do_work(unsigned long);
//中断底半部处理函数:
void xxx_do_work(unsigned long)
{
......
}
//中断顶半部处理函数:
irqreturn_t xxx_interrupt(int irq, void* dev_id, struct pt_regs* regs)
{
//顶半部处理代码
......
//调度工作队列
schedule_work(&xxx_wq);
//剩余处理代码
......
}
//设备驱动模块加载函数
int __init xxx_init(void)
{
......
//申请中断
result = request_irq(xxx_irq, xxx_interrupt, SA_INTERRUPT, "xxx", NULL);
......
//初始化工作队列
INIT_WORK(&xxx_wq, (void (*)(void*))xxx_do_work, NULL);
......
}
//设备驱动模块卸载函数
void __exit xxx_exit(void)
{
......
//释放中断
free_irq(xxx_irq, xxx_interrupt);
......
}
例子:
1、使用系统范围内共享的工作队列的例子:
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/err.h>
//工作队列相关
#include <linux/workqueue.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("*************");
MODULE_VERSION("2.6.35.000");
static int flag = 0;
//STEP1:实现工作队列处理函数;
void my_do_work(struct work_struct* work)
{
flag = 1;
printk("%s:hello, my work queue function\n", __FUNCTION__);
};
//STEP2:定义工作结构体对象
static struct work_struct my_work;
static int thread_process(void* param)
{
while(1)
{
set_current_state(TASK_UNINTERRUPTIBLE);
if(kthread_should_stop())
{
printk("kernel thread should stop;file:%s;line:%d\n", __FILE__, __LINE__);
break;
}
mdelay(10*HZ);
//STEP4:调度工作结构体对象执行:把工作结构体对象添加到系统范围内共享的工作队列中等待执行;
if(0 == flag)
{
schedule_work(&my_work);
}
else
{
flag = 0;
}
}
return 123;
};
static struct task_struct* my_thread = NULL;
static int __init study_init(void)
{
int err = 0;
printk("%s\n", __PRETTY_FUNCTION__);
//STEP3:初始化工作结构体对象
INIT_WORK(&my_work, my_do_work);
my_thread = kthread_create(thread_process, NULL, "my_thread");
if(IS_ERR(my_thread))
{
err = PTR_ERR(my_thread);
my_thread = NULL;
printk(KERN_ERR "unable to start kernel thread:%d\n", err);
return err;
}
wake_up_process(my_thread);
printk("kernel thread start;file:%s;line:%d\n", __FILE__, __LINE__);
return 0;
}
static void __exit study_exit(void)
{
int ret = -1;
printk("%s\n",__PRETTY_FUNCTION__);
if(my_thread)
{
ret = kthread_stop(my_thread);
my_thread = NULL;
}
printk("kernel thread stop,exit code is %d;file:%s;line:%d\n",ret, __FILE__, __LINE__);
}
module_init(study_init);
module_exit(study_exit);
2、使用自己创建的工作队列的例子:
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/err.h>
//工作队列相关
#include <linux/workqueue.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("*************");
MODULE_VERSION("2.6.35.000");
static int flag = 0;
//STEP1:实现工作队列处理函数;
void my_udf_do_work(struct work_struct* work)
{
flag = 1;
printk("%s:hello, my udf work queue function\n", __FUNCTION__);
};
//STEP2:定义工作结构体对象;
static struct work_struct my_udf_work;
//STEP3:定义工作队列指针;
static struct workqueue_struct* lp_wq = NULL;
static int thread_process(void* param)
{
while(1)
{
set_current_state(TASK_UNINTERRUPTIBLE);
if(kthread_should_stop())
{
printk("kernel thread should stop;file:%s;line:%d\n", __FILE__, __LINE__);
break;
}
mdelay(10*HZ);
//STEP6:调度工作结构体对象执行:把工作结构体对象添加到自己创建的工作队列中等待执行;
if(0 == flag)
{
queue_work(lp_wq, &my_udf_work);
}
else
{
flag = 0;
}
}
return 123;
};
static struct task_struct* my_thread = NULL;
static int __init study_init(void)
{
int err = 0;
printk("%s\n", __PRETTY_FUNCTION__);
//STEP4:初始化工作结构体对象;
INIT_WORK(&my_udf_work, my_udf_do_work);
//STEP5:创建自己的工作队列对象(工作者线程对象);
lp_wq = NULL;
lp_wq = create_workqueue("my_work_queue");
//lp_wq = create_singlethread_workqueue("my_work_queue");
if(!lp_wq)
{
printk(KERN_ERR "create my work queueu failed\n");
return -1;
}
my_thread = kthread_create(thread_process, NULL, "my_thread");
if(IS_ERR(my_thread))
{
err = PTR_ERR(my_thread);
my_thread = NULL;
printk(KERN_ERR "unable to start kernel thread:%d\n", err);
return err;
}
wake_up_process(my_thread);
printk("kernel thread start;file:%s;line:%d\n", __FILE__, __LINE__);
return 0;
}
static void __exit study_exit(void)
{
int ret = -1;
printk("%s\n",__PRETTY_FUNCTION__);
if(my_thread)
{
ret = kthread_stop(my_thread);
my_thread = NULL;
}
//STEP7:释放自己创建的工作队列;
destroy_workqueue(lp_wq);
lp_wq = NULL;
printk("kernel thread stop,exit code is %d;file:%s;line:%d\n",ret, __FILE__, __LINE__);
}
module_init(study_init);
module_exit(study_exit);