手把手教你写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>
基本的数据结构:
- //工作队列结构
- struct work_struct {
- atomic_long_t data;
- //链表处理
- struct list_head entry;
- //工作处理函数
- work_func_t func;
- #ifdef CONFIG_LOCKDEP
- struct lockdep_map lockdep_map;
- #endif
- };
当然,如果需要等待一定时间后再执行工作队列,可以用下面这个结构体申请一个内核定时器:
- //指定时间让工作队列执行
- struct delayed_work {
- //初始化
- struct work_struct work;
- //内核定时器
- struct timer_list timer;
- };
一般,不要轻易的去使用工作队列,因为每当创建一条工作队列,内核就会为这条工作队列创建一条内核线程。
工作队列位于进程上下文,与软中断,tasklet有所区别,工作队列里允许延时,睡眠操作,而软中断,tasklet位于中断上下文,不允许睡眠,延时操作。
参考我转发的这位博主写的工作队列和tasklet的区别:
http://blog.csdn.net/morixinguan/article/details/69666642
工作队列(work queue)是另外一种将工作推后执行的形式,它和前面讨论的tasklet有所不同。工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。这样,通过工作队列执行的代码能占尽进程上下文的所有优势。最重要的就是工作队列允许被重新调度甚至是睡眠。
那么,什么情况下使用工作队列,什么情况下使用tasklet。如果推后执行的任务需要睡眠,那么就选择工作队列;如果推后执行的任务不需要睡眠,那么就选择tasklet。另外,如果需要用一个可以重新调度的实体来执行你的下半部处理,也应该使用工作队列。它是唯一能在进程上下文运行的下半部实现的机制,也只有它才可以睡眠。这意味着在需要获得大量的内存时、在需要获取信号量时,在需要执行阻塞式的I/O操作时,它都会非常有用。如果不需要用一个内核线程来推后执行工作,那么就考虑使用tasklet。
接下来我们看看需要使用到哪些API:
- 创建一个队列就会有一个内核线程,一般不要轻易创建队列
- 位于进程上下文--->可以睡眠
- 定义:
- struct work_struct work;
- 初始化:
- INIT_WORK(struct work_struct *work, void (*func)(struct work_struct *work));
- 定义并初始化:
- DECLARE_WORK(name, void (*func)(struct work_struct *work));
- ===========================================================
- 调度:
- int schedule_work(struct work_struct *work);
- 返回1成功, 0已经添加在队列上
- 延迟调度:
- int schedule_delayed_work(struct work_struct *work, unsigned long delay);
- ===========================================================
- 创建新队列和新工作者线程:
- struct workqueue_struct *create_workqueue(const char *name);
- 调度指定队列:
- int queue_work(struct workqueue_struct *wq, struct work_struct *work);
- 延迟调度指定队列:
- int queue_delayed_work(struct workqueue_struct *wq,
- struct work_struct *work, unsigned long delay);
- 销毁队列:
- void destroy_workqueue(struct workqueue_struct *wq);
接下来,我们来实现这个demo:
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/init.h>
- #include <linux/platform_device.h>
- #include <linux/fb.h>
- #include <linux/backlight.h>
- #include <linux/err.h>
- #include <linux/pwm.h>
- #include <linux/slab.h>
- #include <linux/miscdevice.h>
- #include <linux/delay.h>
- #include <linux/gpio.h>
- #include <mach/gpio.h>
- #include <plat/gpio-cfg.h>
- #include <linux/timer.h> /*timer*/
- #include <asm/uaccess.h> /*jiffies*/
- #include <linux/delay.h>
- #include <linux/interrupt.h>
- #include <linux/workqueue.h>
- struct tasklet_struct task_t ;
- struct workqueue_struct *mywork ;
- //定义一个工作队列结构体
- struct work_struct work;
- static void task_fuc(unsigned long data)
- {
- if(in_interrupt()){
- printk("%s in interrupt handle!\n",__FUNCTION__);
- }
- }
- //工作队列处理函数
- static void mywork_fuc(struct work_struct *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);
- //调度工作
- schedule_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);
- printk("irq_key init\n");
- irq_num1 = gpio_to_irq(EXYNOS4_GPX3(2));
- err = request_irq(irq_num1,irq_fuction,IRQF_TRIGGER_FALLING,"tiny4412_key1",(void *)"key1");
- 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");
将程序编译完,将zImage下到板子上:
我们可以看到,当我们按下按键的时候,进入外部中断服务函数,此时task_fuc先被调用,然后调用到mywork_fuc,并打印了mywork_fuc里面的信息,从这里我们用程序验证了,工作队列是位于进程上下文,而不是中断上下文,和tasklet是有所区别的。
手把手教你写Linux设备驱动---中断(三)--workqueue实现(基于友善之臂4412开发板) 【转】的更多相关文章
- 手把手教你从零实现Linux misc设备驱动一(基于友善之臂4412开发板)
关于怎样来写一个misc设备,在前面有篇文章已经介绍了大致的流程,如今就让我们来实现一个最简单的misc设备驱动. http://blog.csdn.net/morixinguan/article/d ...
- 教你写Linux设备驱动程序:一个简短的教程
教你写Linux设备驱动程序:一个简短的教程 http://blog.chinaunix.net/uid-20799298-id-99675.html
- linux设备驱动第三篇:如何写一个简单的字符设备驱动?
在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存 ...
- linux设备驱动第三篇:写一个简单的字符设备驱动
在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分 ...
- linux设备驱动第三篇:如何实现一个简单的字符设备驱动
在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存 ...
- 手把手教你写电商爬虫-第三课 实战尚妆网AJAX请求处理和内容提取
版权声明:本文为博主原创文章,未经博主允许不得转载. 系列教程: 手把手教你写电商爬虫-第一课 找个软柿子捏捏 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫 看完两篇,相信大家已经从开始的 ...
- 友善之臂tiny4412-1306开发板安卓系统烧写
折腾了很久,终于烧写成功.不废话,咱们说说流程吧. 首先,我们需要有一个基于tiny4412的kernel,从友善之臂官网获取. 然后解压: 1.tar -xvf linux-3.5 .... 然后 ...
- linux设备驱动第五篇:驱动中的并发与竟态
综述 在上一篇介绍了linux驱动的调试方法,这一篇介绍一下在驱动编程中会遇到的并发和竟态以及如何处理并发和竞争. 首先什么是并发与竟态呢?并发(concurrency)指的是多个执行单元同时.并行被 ...
- Linux设备驱动开发环境的搭建(转)
经过两周的摸索,终于对Linux设备驱动开发有了个初步的认识,下面对Linux设备驱动开发环境的搭建做个小结,以方便自己以后查询,同时也能给同道的初学者一点帮助. 刚接触Linux设备驱动时,初学者往 ...
随机推荐
- asp.net获取文件绝对路径
一般我们在asp.net中使用HttpContext.Current.Request.MapPath或者 HttpContext.Current.Server.MapPath来获取文件的绝对路径, p ...
- Hessian矩阵【转】
http://blog.sina.com.cn/s/blog_7e1ecaf30100wgfw.html 在数学中,海塞矩阵是一个自变量为向量的实值函数的二阶偏导数组成的方块矩阵,一元函数就是二阶导, ...
- Sparse autoencoder implementation 稀疏自编码器实现
任务:在这个问题中,你将实现稀疏自编码器算法,并且展示它怎么发现边缘是自然图像的一个好的表示. 在文件 sparseae_exercise.zip中,我们已经提供了一些Matlab中的初始代码,你应该 ...
- 【题解】Atcoder ARC#90 E-Avoiding Collision
自己做出来固然开心,但是越发感觉到自己写题的确是很慢很慢了……往往有很多的细节反反复复的考虑才能确定,还要加油呀~ 这道题目的突破口在于正难则反.直接求有多少不相交的不好求,我们转而求出所有相交的.我 ...
- BZOJ4571:[SCOI2016]美味——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=4571 https://www.luogu.org/problemnew/show/P3293 一家 ...
- 弱校的ACM奋斗史
这是一篇老文章,不过由于无法找到最初的发文地址,这里就不能粘贴原文网址了.本站转载此文与ACMer们共勉.感谢acmerfight供稿. 题解:还记得2年前的一个晚上,我和一个女孩一起写完了这篇文章. ...
- thread-wait/sleep
对于sleep()方法,我们首先要知道该方法是属于Thread类中的.而wait()方法,则是属于Object类中的. sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监 ...
- float,absolute脱离文档流的总结
dom元素脱离文档流,有如下几种方式: 1. float 脱离文档流,其他dom元素无视他,在其下方布局,但是其未脱离文本流,其他元素的文本会认为他存在,环绕他布局.父元素会无视他,因此无法获取其高度 ...
- Codeforces VK Cup Finals #424 Div.1 C. Bamboo Partition(数论)
题目要求符合以下条件的最大的d 化简得 注意到 最多只有2*sqrt(a[i]-1)种取值,也就是一共最多有n*sqrt(10^19)种取值,于是枚举一下d,计算出符合上上式的最大的d更新答案,然后d ...
- Ubuntu安装CUDA9.0 + cuDNN
本篇文章是基于安装CUDA 9.0的经验写,CUDA9.0目前支持Ubuntu16.04和Ubuntu17.04两个版本,如下图所示(最下面的安装方式我们选择第一个,即runfile方式): 下载链接 ...