手把手教你写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>

基本的数据结构:

[cpp]  view plain  copy print?
  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. };  

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

[cpp]  view plain  copy print?
  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:

 

[cpp]  view plain  copy print?
  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:

 

[cpp]  view plain  copy print?
  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!\n",__FUNCTION__);  
  28.         }  
  29. }  
  30. //工作队列处理函数  
  31. static 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.   
  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!\n",__FUNCTION__);  
  47.     }  
  48.     printk("key_irq:%d\n",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\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.   
  74. static 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.   
  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是有所区别的。





本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sky-heaven/p/7237911.html,如需转载请自行联系原作者

相关文章
|
1月前
|
安全 Linux 网络虚拟化
Linux网络名称空间和Veth虚拟设备的关系
在讨论Linux网络名称空间和veth(虚拟以太网对)之间的关系时,我们必须从Linux网络虚拟化的核心概念开始。Linux网络名称空间和veth是Linux网络虚拟化和容器化技术的重要组成部分,它们之间的关系密不可分,对于构建隔离、高效的网络环境至关重要。😊
|
1月前
|
Linux 网络安全 网络虚拟化
Linux虚拟网络设备:底层原理与性能优化深度解析
在深入探讨Linux虚拟网络设备的底层原理之前,重要的是要理解这些设备如何在Linux内核中实现,以及它们如何与操作系统的其他部分交互以提供高效且灵活的网络功能。虚拟网络设备在现代网络架构中发挥着关键作用🔑,特别是在云计算☁️、容器化📦和网络功能虚拟化(NFV)环境中。
Linux虚拟网络设备:底层原理与性能优化深度解析
|
1月前
|
Linux 网络虚拟化 虚拟化
Linux虚拟网络设备深度解析:使用场景、分类与开发者指南
Linux虚拟网络设备支撑着各种复杂的网络需求和配置,从基础的网络桥接到高级的网络隔离和加密🔐。以下是对主要Linux虚拟网络设备的介绍、它们的作用以及适用场景的概览,同时提出了一种合理的分类,并指出应用开发人员应该着重掌握的设备。
Linux虚拟网络设备深度解析:使用场景、分类与开发者指南
|
1月前
|
安全 Linux API
Linux设备模型统一:桥接硬件多样性与应用程序开发的关键
在Linux的宏大世界中,各种各样的硬件设备如星辰般繁多。从常见的USB设备到复杂的网络接口卡,从嵌入式设备到强大的服务器,Linux需要在这些差异极大的硬件上运行。这就引出了一个问题:Linux是如何统一这些不同硬件的设备模型的呢?本文将探讨Linux是如何针对不同的硬件统一设备模型的,这一统一的设备模型对于应用程序开发人员来说又有何意义。让我们一探究竟🕵️‍♂️。
Linux设备模型统一:桥接硬件多样性与应用程序开发的关键
|
1月前
|
Cloud Native Linux 网络虚拟化
深入理解Linux veth虚拟网络设备:原理、应用与在容器化架构中的重要性
在Linux网络虚拟化领域,虚拟以太网设备(veth)扮演着至关重要的角色🌐。veth是一种特殊类型的网络设备,它在Linux内核中以成对的形式存在,允许两个网络命名空间之间的通信🔗。这篇文章将从多个维度深入分析veth的概念、作用、重要性,以及在容器和云原生环境中的应用📚。
深入理解Linux veth虚拟网络设备:原理、应用与在容器化架构中的重要性
|
1月前
|
监控 中间件 Linux
深入Linux设备模型:开发者指南
Linux的设备模型是操作系统管理硬件设备的一种高级抽象,它不仅涉及到设备驱动程序的加载和卸载,还包括设备之间的关系、设备的状态管理以及与用户空间通信的机制。理解Linux的设备模型对于应用开发人员来说至关重要,它有助于开发出更加稳定、高效的应用程序。🌟
深入Linux设备模型:开发者指南
|
3天前
|
网络协议 Shell Linux
LabVIEW 在NI Linux实时设备上访问Shell
LabVIEW 在NI Linux实时设备上访问Shell
|
4天前
|
存储 负载均衡 网络协议
X86 linux异常处理与Ipipe接管中断/异常
本文讲述了X86平台上Xenomai的ipipe如何接管中断处理。首先回顾了X86中断处理机制,包括IDT(中断描述符表)的工作原理和中断处理流程。接着详细介绍了Linux中中断门的初始化,包括门描述符的结构、中断门的定义和填充,以及IDT的加载。在异常处理部分,文章讲解了早期异常处理和start_kernel阶段的异常向量初始化。最后,讨论了APIC和SMP中断在IDT中的填充,以及剩余中断的统一处理。文章指出,ipipe通过在中断入口处插入`__ipipe_handle_irq()`函数,实现了对中断的拦截和优先处理,确保了实时性。
9 0
X86 linux异常处理与Ipipe接管中断/异常
|
6天前
|
Linux
linux驱动层输出dev_dbg打印信息
linux驱动层输出dev_dbg打印信息
11 0
|
15天前
|
存储 监控 Linux
【专栏】在 Linux 中,了解已安装驱动器是系统管理的关键
【4月更文挑战第28天】在 Linux 中,了解已安装驱动器是系统管理的关键。本文介绍了三种方法:1) 使用 `lsblk` 命令显示设备名、大小和类型;2) `fdisk -l` 命令提供详细分区信息;3) `gnome-disks` 等系统管理工具展示驱动器信息。此外,还讨论了驱动器类型识别、挂载点概念及其应用。通过这些方法,用户能有效地监控和管理 Linux 系统中的驱动器。