• 手把手教你写Linux设备驱动---中断(三)--workqueue实现(基于友善之臂4412开发板) 【转】


    转自:http://blog.csdn.net/morixinguan/article/details/69680909

    上节,我们讲到如何来实现tasklet小任务机制

    http://blog.csdn.NET/morixinguan/article/details/69666935

    这节,我们来实现一下中断下半部的工作队列:

    在写这个demo之前,我们要了解一下工作队列的相关数据结构还有API。

    需要包含的头文件:

    #include <Linux/workqueue.h>

    基本的数据结构:

    1. //工作队列结构  
    2. struct 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. };  

    当然,如果需要等待一定时间后再执行工作队列,可以用下面这个结构体申请一个内核定时器:

    1. //指定时间让工作队列执行  
    2. struct delayed_work {  
    3.     //初始化  
    4.     struct work_struct work;  
    5.     //内核定时器  
    6.     struct timer_list timer;  
    7. };  

    一般,不要轻易的去使用工作队列,因为每当创建一条工作队列,内核就会为这条工作队列创建一条内核线程。

    工作队列位于进程上下文,与软中断,tasklet有所区别,工作队列里允许延时,睡眠操作,而软中断,tasklet位于中断上下文,不允许睡眠,延时操作。

    参考我转发的这位博主写的工作队列和tasklet的区别:

    http://blog.csdn.net/morixinguan/article/details/69666642

    工作队列(work queue)是另外一种将工作推后执行的形式,它和前面讨论的tasklet有所不同。工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。这样,通过工作队列执行的代码能占尽进程上下文的所有优势。最重要的就是工作队列允许被重新调度甚至是睡眠
          那么,什么情况下使用工作队列,什么情况下使用tasklet。如果推后执行的任务需要睡眠,那么就选择工作队列;如果推后执行的任务不需要睡眠,那么就选择tasklet。另外,如果需要用一个可以重新调度的实体来执行你的下半部处理,也应该使用工作队列。它是唯一能在进程上下文运行的下半部实现的机制,也只有它才可以睡眠。这意味着在需要获得大量的内存时、在需要获取信号量时,在需要执行阻塞式的I/O操作时,它都会非常有用。如果不需要用一个内核线程来推后执行工作,那么就考虑使用tasklet。

    接下来我们看看需要使用到哪些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);  

    接下来,我们来实现这个demo:

    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>  
    20. struct tasklet_struct task_t ;   
    21. struct workqueue_struct *mywork ;  
    22. //定义一个工作队列结构体  
    23. struct work_struct work;  
    24. static void task_fuc(unsigned long data)  
    25. {  
    26.     if(in_interrupt()){  
    27.              printk("%s in interrupt handle! ",__FUNCTION__);  
    28.         }  
    29. }  
    30. //工作队列处理函数  
    31. static void mywork_fuc(struct work_struct *work)  
    32. {  
    33.     if(in_interrupt()){  
    34.              printk("%s in interrupt handle! ",__FUNCTION__);  
    35.         }  
    36.     msleep(2);  
    37.     printk("%s in process handle! ",__FUNCTION__);  
    38. }  
    39.   
    40. static 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! ",__FUNCTION__);  
    47.     }  
    48.     printk("key_irq:%d ",irq);  
    49.     return IRQ_HANDLED ;  
    50. }  
    51.       
    52. static 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 ");  
    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.   
    74. static void __exit tiny4412_Key_irq_test_exit(void)   
    75. {  
    76.     int irq_num1 ;  
    77.     printk("irq_key exit ");  
    78.     irq_num1 = gpio_to_irq(EXYNOS4_GPX3(2));  
    79.     //销毁一条工作队列  
    80.     destroy_workqueue(mywork);  
    81.     free_irq(irq_num1,(void *)"key1");  
    82. }  
    83.   
    84. module_init(tiny4412_Key_irq_test_init);  
    85. module_exit(tiny4412_Key_irq_test_exit);  
    86.   
    87. MODULE_LICENSE("GPL");  
    88. MODULE_AUTHOR("YYX");  
    89. MODULE_DESCRIPTION("Exynos4 KEY Driver");  

    将程序编译完,将zImage下到板子上:


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

  • 相关阅读:
    GridControl控件绑定RepositoryItemImageComboBox 作为下拉框使用
    ASP.NET MVC 扩展数据验证 转
    ASP.NET MVC Razor HtmlHelper扩展和自定义控件
    Thread锁 Monitor类、Lock关键字和Mutex类
    C# Thread.Join()用法的理解 转
    Redis集群高可用
    .Net Core中GC分析及调优总结-干货
    数据库之MySQL部署MGC方案(四)
    数据库之MySQL部署PXC方案(三)
    数据库之MySQL部署MGR方案(二)
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/7237911.html
Copyright © 2020-2023  润新知