• Linux设备驱动workqueue(工作队列)案例实现


     

    一、Linux工作队列与Linux小任务机制的区别

    工作队列(work queue)是另外一种将工作推后执行的形式,tasklet(小任务机制)有所不同。工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。这样,通过工作队列执行的代码能占尽进程上下文的所有优势。最重要的就是工作队列允许被重新调度甚至是睡眠。

    那么,什么情况下使用工作队列,什么情况下使用tasklet呢?如果推后执行的任务需要睡眠,那么就选择工作队列;如果推后执行的任务不需要睡眠,那么就选择tasklet。另外,如果需要用一个可以重新调度的实体来执行你的下半部处理,也应该使用工作队列。它是唯一能在进程上下文运行的下半部实现的机制,也只有它才可以睡眠。这意味着在需要获得大量的内存时、在需要获取信号量时,在需要执行阻塞式的I/O操作时,它都会非常有用。如果不需要用一个内核线程来推后执行工作,那么就考虑使用tasklet。

    一般,不要轻易的去使用工作队列,因为每当创建一条工作队列,内核就会为这条工作队列创建一条内核线程。工作队列位于进程上下文,与软中断,tasklet有所区别,工作队列里允许延时,睡眠操作,而软中断,tasklet位于中断上下文,不允许睡眠和延时操作。

    二、使用Linux工作队列

    1、需要包含的头文件

    #include <linux/workqueue.h>

    2、工作队列相关的数据结构(各个版本内核可能不同,这里用的是3.5)

     1//工作队列结构
     2struct work_struct {
     3    atomic_long_t data;
     4    //链表处理
     5    struct list_head entry;
     6    //工作处理函数
     7    work_func_t func;
     8#ifdef CONFIG_LOCKDEP
     9    struct lockdep_map lockdep_map;
    10#endif
    11};

    3、操作工作队列相关的API

     1创建一个队列就会有一个内核线程,一般不要轻易创建队列
     2位于进程上下文--->可以睡眠
     3定义:
     4    struct work_struct work;
     5
     6初始化:
     7    INIT_WORK(struct work_struct *work, void (*func)(struct work_struct *work));
     8
     9定义并初始化:
    10    DECLARE_WORK(name, void (*func)(struct work_struct *work));
    11
    12===========================================================
    13
    14调度:
    15    int schedule_work(struct work_struct *work);
    16    返回1成功, 0已经添加在队列上
    17
    18延迟调度:
    19    int schedule_delayed_work(struct work_struct *work, unsigned long delay);
    20
    21===========================================================
    22
    23创建新队列和新工作者线程:
    24    struct workqueue_struct *create_workqueue(const char *name);
    25
    26调度指定队列:
    27    int queue_work(struct workqueue_struct *wq, struct work_struct *work);
    28
    29延迟调度指定队列:
    30    int queue_delayed_work(struct workqueue_struct *wq, 
    31            struct work_struct *work, unsigned long delay);
    32销毁队列:
    33    void destroy_workqueue(struct workqueue_struct *wq);

    4、Demo实现(基于Tiny4412 Linux3.5内核)

    1#include <linux/module.h>
     2#include <linux/kernel.h>
     3#include <linux/init.h>
     4#include <linux/platform_device.h>
     5#include <linux/fb.h>
     6#include <linux/backlight.h>
     7#include <linux/err.h>
     8#include <linux/pwm.h>
     9#include <linux/slab.h>
    10#include <linux/miscdevice.h>
    11#include <linux/delay.h>
    12#include <linux/gpio.h>
    13#include <mach/gpio.h>
    14#include <plat/gpio-cfg.h>
    15#include <linux/timer.h>  /*timer*/
    16#include <asm/uaccess.h>  /*jiffies*/
    17#include <linux/delay.h>
    18#include <linux/interrupt.h>
    19#include <linux/workqueue.h>
    20struct tasklet_struct task_t ; 
    21struct workqueue_struct *mywork ;
    22//定义一个工作队列结构体
    23struct work_struct work;
    24static void task_fuc(unsigned long data)
    25{
    26    if(in_interrupt()){
    27             printk("%s in interrupt handle!\n",__FUNCTION__);
    28        }
    29}
    30//工作队列处理函数
    31static void mywork_fuc(struct work_struct *work)
    32{
    33    if(in_interrupt()){
    34             printk("%s in interrupt handle!\n",__FUNCTION__);
    35        }
    36    msleep(2);
    37    printk("%s in process handle!\n",__FUNCTION__);
    38}
    39
    40static irqreturn_t irq_fuction(int irq, void *dev_id)
    41{    
    42    tasklet_schedule(&task_t);
    43    //调度工作
    44    schedule_work(&work);
    45    if(in_interrupt()){
    46         printk("%s in interrupt handle!\n",__FUNCTION__);
    47    }
    48    printk("key_irq:%d\n",irq);
    49    return IRQ_HANDLED ;
    50}
    51
    52static int __init tiny4412_Key_irq_test_init(void) 
    53{
    54    int err = 0 ;
    55    int irq_num1 ;
    56    int data_t = 100 ;
    57    //创建新队列和新工作者线程
    58    mywork = create_workqueue("my work");
    59    //初始化
    60    INIT_WORK(&work,mywork_fuc);
    61    //调度指定队列
    62    queue_work(mywork,&work);
    63    tasklet_init(&task_t,task_fuc,data_t);
    64    printk("irq_key init\n");
    65    irq_num1 = gpio_to_irq(EXYNOS4_GPX3(2));
    66    err = request_irq(irq_num1,irq_fuction,IRQF_TRIGGER_FALLING,"tiny4412_key1",(void *)"key1");
    67    if(err != 0){
    68        free_irq(irq_num1,(void *)"key1");
    69        return -1 ;
    70    }
    71    return 0 ;
    72}
    73
    74static void __exit tiny4412_Key_irq_test_exit(void) 
    75{
    76    int irq_num1 ;
    77    printk("irq_key exit\n");
    78    irq_num1 = gpio_to_irq(EXYNOS4_GPX3(2));
    79    //销毁一条工作队列
    80    destroy_workqueue(mywork);
    81    free_irq(irq_num1,(void *)"key1");
    82}
    83
    84module_init(tiny4412_Key_irq_test_init);
    85module_exit(tiny4412_Key_irq_test_exit);
    86
    87MODULE_LICENSE("GPL");
    88MODULE_AUTHOR("YYX");
    89MODULE_DESCRIPTION("Exynos4 KEY Driver");

    将程序编译完,将zImage下到板子上,重新启动会看到内核打印信息

    可以看到,当我们按下按键的时候,进入外部中断服务函数,此时task_fuc先被调用,然后调用到mywork_fuc,并打印了mywork_fuc里面的信息,从这里我们用程序验证了,工作队列是位于进程上下文,而不是中断上下文,和tasklet是有所区别的,下一节我们将会讲一讲tasklet(小任务机制)。

  • 相关阅读:
    串口操作
    图片转化成二进制数据、等比缩放
    DSO Framer Control Object 操作word文件
    C#图片存入数据库及其读出显示
    对话框的用法
    C#读取数据库中的表
    将Resource中的图片资源动态绑定到PictureBox中:
    ProgressBar
    C# 操作数据库表和数据库
    操作系统–进程管理
  • 原文地址:https://www.cnblogs.com/dream397/p/15606328.html
Copyright © 2020-2023  润新知