work queue

1、工作队列

schedule_work把任务提交到内核默认提供的工作队列[events/0]中执行
schedule_delayed_work把任务提交到内核默认提供的工作队列[events/0]中,(延时一定的时间)执行
queue_work把任务提交到自定义创建的队列[my workqueue/0]中执行
queue_delayed_work把任务提交到自定义创建的队列[my workqueue/0]中,(延时一定的时间)执行

1.1 queue_work(自定义的工作队列)

delayed_workqueue的使用步骤如下:
(1)具体的work:

1)定义workqueue要做的delayed工作:struct delayed_work mdelayed_work;
2)初始化workqueue:INIT_DELAYED_WORK(mworkqueue, mdelayed_work);

(2)存放work 的队列:

1)定义workqueue: struct workqueue_struct *mworkqueue;
2)创建线程queue并加以名字:mworkqueue = create_singlethread_workqueue("myqueue");

(3)在需要执行的地方调用work:

运行queue:queue_delayed_work(mworkqueue, mdelayed_work, delay_time);

1.1.1 queue_delayed_work(延时)

(1)设备结构体里面定义工作队列:

  struct workqueue_struct *sm_usb_wq;		//1.自定义工作队列,存放work
  struct delayed_work	sm_work;			//1.定义延时工作,具体的work,结构体对象私有

在这里插入图片描述
(2)初始化工作队列:

INIT_DELAYED_WORK(&mdwc->sm_work, dwc3_otg_sm_work);	//2.将工作队列和其function关联
sm_usb_wq = alloc_ordered_workqueue("k_sm_usb", WQ_FREEZABLE);		//3.创建新队列和新工作者线程

在这里插入图片描述
(3)function函数:
在这里插入图片描述
(4)调用工作队列:
在这里插入图片描述

1.1.2 queue_delayed_work(实现循环)

如果要实现循环任务,可以在delayed_work_func中将delayed_workqueue再次添加到queue中,即再次调用queue_delayed_work

#include <linux/sched.h>
#include <linux/init.h>   
#include <linux/module.h> 
#include <linux/workqueue.h> 
#include <linux/proc_fs.h>
#include <linux/delay.h>   
#include <linux/interrupt.h>    
#include <linux/kernel.h> 

//1.定义workqueue要做的delayed工作
struct delayed_work mdwq; 
 
//2.定义workqueue
struct workqueue_struct *mwq; 
 
void delay_work_func(struct work_struct *work)  
{  
    int i;  
    printk(KERN_INFO "%s:%d\n",__FUNCTION__,__LINE__);  
    for (i = 0; i < 3; i++) {  
        printk(KERN_ERR "%s:i=%d\n",__FUNCTION__,i);  
        msleep(3000);  
	}
	//6.实现循环实行任务
	queue_delayed_work(mwq, &mdwq, 3000);
}  
  
static int __init delay_work_init(void)  
{  
    int ret;  
	int i;  
	
	//3. 创建线程queue并加以名字
    mwq = create_workqueue("my workqueue");  
    if (!mwq) {  
        printk(KERN_ERR "Create workqueue failed!\n");  
        return 1;     
    }  
    printk(KERN_INFO "Create workqueue successful!\n");  
  
	//4. 初始化workqueue
    INIT_DELAYED_WORK(&mdwq, delay_work_func);  
      
	//5. 运行queue
    ret = queue_delayed_work(mwq, &mdwq, 3000);  
    printk(KERN_INFO "first ret=%d!\n", ret);  
      
    for (i = 0; i < 3; i++) {   
        printk(KERN_INFO "%s:ret=%d,i=%d\n",__FUNCTION__,ret, i);  
        msleep(1000);  
    }  
  
    ret = queue_delayed_work(mwq, &mdwq, 0);  
    printk(KERN_INFO "second ret=%d!\n", ret);  
  
    return 0;  
}  
  
static void __exit delay_work_exit(void)  
{  
    int ret;  
    ret = cancel_delayed_work(&mdwq);  
    flush_workqueue(mwq);  
    destroy_workqueue(mwq);  
    printk(KERN_INFO "Exit! ret=%d\n", ret);  
}  
module_init(delay_work_init);  
module_exit(delay_work_exit);  
MODULE_LICENSE("GPL");

运行结果:

kernel: Create workqueue successful!  
kernel: first ret=1!  
kernel: delay_work_init:ret=1,i=0  
kernel: delay_work_init:ret=1,i=1  
kernel: delay_work_init:ret=1,i=2  
kernel: second ret=0!  
kernel: Exit! ret=1  

从例子可以看出:
(1)当工作队列还在执行该任务,调用queue_delayed_work()返回1,否则返回0。
(2)主线程mwq将任务添加到工作队列后,使得工作队列在延迟delay后执行函数delay_work_func(),而mwq线程继续执行。

1.2 schedule_work(内核工作队列)

内核自带有系统的workqueue(system_wq),使用schedule_work 将work 添加到内核工作队列去执行。

static inline bool schedule_work(struct work_struct *work)
{
	return queue_work(system_wq, work);
}

使用步骤:

  1. 定义工作:struct work_struct xxx_work;
  2. 初始化工作,定义执行函数:INIT_WORK(&xxx_wq, xxx_do_work);
  3. 调度工作:schedule_work(&xxx_wq);

例子:

//1.定义工作
struct work_struct xxx_wq;

//2.定义工作执行函数,处理中断底半部
void xxx_do_work (struct work_struct *work)
{
	...
}

//中断顶半部
irqreturn_t xxx_interrupt (int irq, void *dev_id)
{
	...
	//4.调度工作
	schedule_work(&xxx_wq);
	...
	return IRQ_HANDLED;
} 

//模块加载函数
int xxx_init (void)
{
	...
	//申请中断
	result = request_irq(xxx_irq, xxx_interrupt, 0, "xxx", NULL);
	...
	//3.初始化工作,将工作和执行函数关联
	INIT_WORK(&xxx_wq, xxx_do_work);
	...
}

//模块卸载函数
void xxx_exit(void)
{
	...
	//释放中断
	free_irq(xxx_irq, xxx_interrupt);
	...
}

2、task

2.1 workqueue and task

工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。最重要的就是工作队列允许被重新调度甚至是睡眠

(1)如果推后执行的任务需要睡眠,那么就选择workqueue;如果推后执行的任务不需要睡眠,那么就选择tasklet

(2)如果需要用一个可以重新调度的实体来执行你的下半部处理,也应该使用工作队列。在需要获得大量的内存时、在需要获取信号量时,在需要执行阻塞式的I/O操作时,它都会非常有用。

(3)工作队列位于进程上下文,工作队列里允许延时,睡眠操作;而软中断、tasklet位于中断上下文,不允许睡眠和延时操作。

2.2 task 使用

struct tasklet_struct task_t ;        //定义一个task
struct workqueue_struct *mywork ;     //自定义一个workqueue
struct work_struct work;              //定义一个work  
       
static void task_fuc(unsigned long data)     //task执行函数
{
    if(in_interrupt()){
        printk("%s in interrupt handle!\n",__FUNCTION__);
    }
}

static void mywork_fuc(struct work_struct *work)  //work执行函数
{
    if(in_interrupt()){
        printk("%s in interrupt handle!\n",__FUNCTION__);
    }
    msleep(2);
    printk("%s in process handle!\n",__FUNCTION__);
}

static irqreturn_t irq_fuction(int irq, void *dev_id)   //中断执行函数
{    
    tasklet_schedule(&task_t);  //调度task
    
    schedule_work(&work);       //调度work
    if(in_interrupt()){
         printk("%s in interrupt handle!\n",__FUNCTION__);
    }
    printk("key_irq:%d\n",irq);
    return IRQ_HANDLED ;
}

static int __init tiny4412_Key_irq_test_init(void) 
{
    int err = 0 ;
    int irq_num1 ;
    int data_t = 100 ;
    
    mywork = create_workqueue("my work");   //创建新队列和新工作者线程
    INIT_WORK(&work,mywork_fuc);            //初始化工作和执行函数
    queue_work(mywork,&work);               //调度指定队列
    tasklet_init(&task_t,task_fuc,data_t);  //初始化task
    printk("irq_key init\n");
    irq_num1 = gpio_to_irq(EXYNOS4_GPX3(2));       //gpio初始化irq
    err = request_irq(irq_num1,irq_fuction,IRQF_TRIGGER_FALLING,
                      "tiny4412_key1",(void *)"key1");        //申请irq
    if(err != 0){
        free_irq(irq_num1,(void *)"key1");
        return -1 ;
    }
    return 0 ;
}

static void __exit tiny4412_Key_irq_test_exit(void) 
{
    int irq_num1 ;
    printk("irq_key exit\n");
    irq_num1 = gpio_to_irq(EXYNOS4_GPX3(2));
    
    destroy_workqueue(mywork);       //销毁一条工作队列
    free_irq(irq_num1,(void *)"key1");
}

module_init(tiny4412_Key_irq_test_init);
module_exit(tiny4412_Key_irq_test_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("YYX");
MODULE_DESCRIPTION("Exynos4 KEY Driver");

打印log如下:
在这里插入图片描述
可以看到,当我们按下按键的时候,进入外部中断服务函数,此时task_fuc先被调用,然后调用到mywork_fuc,并打印了mywork_fuc里面的信息,从这里验证了,工作队列是位于进程上下文,而不是中断上下文

  • 1
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值