转自: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. INIT_WORK(struct work_struct *work, void (*func)(struct work_struct *work));
  7. 定义并初始化:
  8. DECLARE_WORK(name, void (*func)(struct work_struct *work));
  9. ===========================================================
  10. 调度:
  11. int schedule_work(struct work_struct *work);
  12. 返回1成功, 0已经添加在队列上
  13. 延迟调度:
  14. int schedule_delayed_work(struct work_struct *work, unsigned long delay);
  15. ===========================================================
  16. 创建新队列和新工作者线程:
  17. struct workqueue_struct *create_workqueue(const char *name);
  18. 调度指定队列:
  19. int queue_work(struct workqueue_struct *wq, struct work_struct *work);
  20. 延迟调度指定队列:
  21. int queue_delayed_work(struct workqueue_struct *wq,
  22. struct work_struct *work, unsigned long delay);
  23. 销毁队列:
  24. 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!\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. static irqreturn_t irq_fuction(int irq, void *dev_id)
  40. {
  41. tasklet_schedule(&task_t);
  42. //调度工作
  43. schedule_work(&work);
  44. if(in_interrupt()){
  45. printk("%s in interrupt handle!\n",__FUNCTION__);
  46. }
  47. printk("key_irq:%d\n",irq);
  48. return IRQ_HANDLED ;
  49. }
  50. static int __init tiny4412_Key_irq_test_init(void)
  51. {
  52. int err = 0 ;
  53. int irq_num1 ;
  54. int data_t = 100 ;
  55. //创建新队列和新工作者线程
  56. mywork = create_workqueue("my work");
  57. //初始化
  58. INIT_WORK(&work,mywork_fuc);
  59. //调度指定队列
  60. queue_work(mywork,&work);
  61. tasklet_init(&task_t,task_fuc,data_t);
  62. printk("irq_key init\n");
  63. irq_num1 = gpio_to_irq(EXYNOS4_GPX3(2));
  64. err = request_irq(irq_num1,irq_fuction,IRQF_TRIGGER_FALLING,"tiny4412_key1",(void *)"key1");
  65. if(err != 0){
  66. free_irq(irq_num1,(void *)"key1");
  67. return -1 ;
  68. }
  69. return 0 ;
  70. }
  71. static void __exit tiny4412_Key_irq_test_exit(void)
  72. {
  73. int irq_num1 ;
  74. printk("irq_key exit\n");
  75. irq_num1 = gpio_to_irq(EXYNOS4_GPX3(2));
  76. //销毁一条工作队列
  77. destroy_workqueue(mywork);
  78. free_irq(irq_num1,(void *)"key1");
  79. }
  80. module_init(tiny4412_Key_irq_test_init);
  81. module_exit(tiny4412_Key_irq_test_exit);
  82. MODULE_LICENSE("GPL");
  83. MODULE_AUTHOR("YYX");
  84. MODULE_DESCRIPTION("Exynos4 KEY Driver");

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

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

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

  1. 手把手教你从零实现Linux misc设备驱动一(基于友善之臂4412开发板)

    关于怎样来写一个misc设备,在前面有篇文章已经介绍了大致的流程,如今就让我们来实现一个最简单的misc设备驱动. http://blog.csdn.net/morixinguan/article/d ...

  2. 教你写Linux设备驱动程序:一个简短的教程

    教你写Linux设备驱动程序:一个简短的教程 http://blog.chinaunix.net/uid-20799298-id-99675.html

  3. linux设备驱动第三篇:如何写一个简单的字符设备驱动?

    在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存 ...

  4. linux设备驱动第三篇:写一个简单的字符设备驱动

          在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分 ...

  5. linux设备驱动第三篇:如何实现一个简单的字符设备驱动

    在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存 ...

  6. 手把手教你写电商爬虫-第三课 实战尚妆网AJAX请求处理和内容提取

    版权声明:本文为博主原创文章,未经博主允许不得转载. 系列教程: 手把手教你写电商爬虫-第一课 找个软柿子捏捏 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫 看完两篇,相信大家已经从开始的 ...

  7. 友善之臂tiny4412-1306开发板安卓系统烧写

    折腾了很久,终于烧写成功.不废话,咱们说说流程吧. 首先,我们需要有一个基于tiny4412的kernel,从友善之臂官网获取. 然后解压: 1.tar -xvf  linux-3.5 .... 然后 ...

  8. linux设备驱动第五篇:驱动中的并发与竟态

    综述 在上一篇介绍了linux驱动的调试方法,这一篇介绍一下在驱动编程中会遇到的并发和竟态以及如何处理并发和竞争. 首先什么是并发与竟态呢?并发(concurrency)指的是多个执行单元同时.并行被 ...

  9. Linux设备驱动开发环境的搭建(转)

    经过两周的摸索,终于对Linux设备驱动开发有了个初步的认识,下面对Linux设备驱动开发环境的搭建做个小结,以方便自己以后查询,同时也能给同道的初学者一点帮助. 刚接触Linux设备驱动时,初学者往 ...

随机推荐

  1. httpservlet在创建实例对象时候默认调用有参数的init方法 destroy()方法 service方法, 父类的init方法给子类实例一个config对象

  2. BZOJ 1046 上升序列(LIS变形)

    要保证长度为L的序列下标字典序最小,当然要尽量选前面的数. 如何判断前面的数是否满足条件?,只需要知道这个数开头的递增序列的最长长度是多少,如果不小于L,那么必然可以加入这个数.还需判断一下它是否大于 ...

  3. Luogu2540 斗地主增强版(搜索+动态规划)

    单纯的暴搜似乎还是很好写的,然而过不了.出完顺子之后答案是可以dp出来的,于是大力搜然后大力dp就好了. dp时强行讨论完了几乎所有拆牌情况,理性愉悦一发. #include<iostream& ...

  4. A表数据插入到B表(表结构不一致)

    D_A  有E\F\H 3字段 D_B 有 A\B\C\D\E\ID 字段 将 D_B 个别字段插入到D_A  表 INSERT INTO  D_A(E,F,H) select B,A,ID from ...

  5. bzoj 3132: 上帝造题的七分钟 (二维树状数组)

    推推公式,最后变成四个东西的前缀和 然后不知道为什么一直wa,数据在本地测是没有错的& 好心的管理员还给了某位p党大神a了的代码,感人肺腑(虽然还是没发现到底我的程序是问题) var f1,f ...

  6. BZOJ3144:[HNOI2013]切糕——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=3144 看着很像网络流,但是费用流貌似无法解决这个问题,其实甚至连忽略d的情况都做不到. 最小割? ...

  7. BZOJ4651 & 洛谷1173 & UOJ220:[NOI2016]网格——题解(附debug数据)

    https://www.lydsy.com/JudgeOnline/problem.php?id=4651 https://www.luogu.org/problemnew/show/P1173#su ...

  8. struts2远程代码执行漏洞汇总整理

    一.S2-001 1.漏洞原理 在默认配置下,如果用户所提交的表单出现验证错误,后端会对用户的输入进行解析处理,然后返回并显示处理结果. 举个例子,当你提交的登录表单为username=xishir& ...

  9. UVA.297 Quadtrees (四分树 DFS)

    UVA.297 Quadtrees (四分树 DFS) 题意分析 将一个正方形像素分成4个小的正方形,接着根据字符序列来判断是否继续分成小的正方形表示像素块.字符表示规则是: p表示这个像素块继续分解 ...

  10. java里的 懒汉和恶汉模式-----讲解

    ------------java中的恶汉模式 public void Test{ private static Test inte = new Test(); // 内部自己创建好实例,私有属性(不建 ...