分享

中断控制---工作队列

 enchen008 2013-12-24

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);

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

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多