工作队列——workqueue 与 tasklet

workqueue与tasklet功能相似,都是让某个函数在将来被调用。但是两者件也有一些区别:

  • tasklet在软件中断上下文中运行,tasklet代码必须是原子的。工作队列在一个特殊的内核线程的上下文中运行,工作队列允许休眠;
  • tasklet始终运行在被初始提交的同一处理器上,工作队列不是一定的;
  • 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:
头文件<linux/workqueue.h>

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)

  1. 静态创建
    DECLARE_WORK(n, f);
    DECLARE_DELAYED_WORK(n, f);
头文件#include <linux/workqueue.h>

#define DECLARE_WORK(n, f)      \
    struct work_struct n = __WORK_INITIALIZER(n, f)

定义并初始化一个叫 n 的work_struct 数据结构,它对应的的处理函数是f。

  1. 动态创建
/*初始化之前需要定义一个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 <linux/module.h>
#include <linux/init.h>

#include <linux/interrupt.h>
#include <linux/workqueue.h>

#define DEBUG_SWITCH 1
#if DEBUG_SWITCH
    #define P_DEBUG(fmt, args...) printk("<1>" "<kernel>[%s]"fmt, __FUNCTI ON__, ##args)
#else
    #define P_DEBUG(fmt, args...) printk("<7>" "<kernel>[%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 <linux/module.h>
#include <linux/init.h>

#include <linux/interrupt.h>
#include <linux/workqueue.h>

#define DEBUG_SWITCH 1
 #if DEBUG_SWITCH
    #define P_DEBUG(fmt, args...) printk("<1>" "<kernel>[%s]"fmt, __FUNCTI ON__, ##args)
 #else
    #define P_DEBUG(fmt, args...) printk("<7>" "<kernel>[%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");
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值