1.1 介绍
Linux将中断分为中断上半部和下半部。上半部用来处理紧急的和硬件操作相关的,此时所有当前中断线都被禁止,包括其它CPU。下半部用来处理能够被允许推迟完成的中断处理部分,此时中断是开启的。上下半部之间的界限依情况划分。
而异常和中断不同,必须考虑时钟的同步,也称同步中断,如除0、缺页等。这里我们只讨论异步中断。
1.2 中断注册/释放
1.2.1 注册函数 request_irq()

irq:要分配的中断号
handler: 中断处理函数,中断触发时会被调用
flags:处理中断标志,比较常用的是IRQF_DISABLED、IRQF_SHARED,IRQF_ONESHOT这几种,IRQF_ONESHOT后面再解释。IRQF_DISABLED表示进入中断处理程序时禁止任何中断,包括其它CPU,默认只是屏蔽当前对应所有CPU的中断线。IRQF_SHARED表示中断线是共享的,也就是说同一个中断线上会对应多个中断处理程序,在中断处理程序中,通过第5个参数dev来判断到底是哪个发生了中断。。
name:中断名。会被/proc/irq和/proc/interrupt使用。
dev: 中断共享时用到,用来判断是哪个中断触发。

request_irq()会导致睡眠,因为其在注册过程中调用了kmalloc(),而这个函数是会引起睡眠的。因此不能用于中断上下文中或者其他不允许阻塞的地方。
1.2.2 注册函数 request_threaded_irq()
考虑到硬中断占用时间问题以及软中断/tasklet的使用死锁及debug问题,内核又新增了一个带线程处理功能的中断接口。它将不重要的工作放到线程处理函数中作了(有点像work queue不是吗)。在未来,有可能tasklet 会被其替代掉。
接口定义:

除了thread_fn,其他的参数和request_irq()中的意义一样。
handler: 中断处理函数,不要可以传NULL。
thread_fn: 中断线程处理函数,不需要可以传NULL。

handler 和 thread_fn 两个可以同时定义,或者只用其中一个。当同时定义时,中断处理函数必须要返回值为IRQ_WAKE_THREAD,thread_fn才能被调用到。thread_fn可以返回IRQ_NONE或者IRQ_HANDLED。
之前有提到一个flag: IRQF_ONESHOT。它表示当中断线程处理函数执行完成以后才开启中断。另外,IRQF_ONESHOT和IRQF_SHARED是不能共用的,因为IRQF_SHARED不能关闭中断。
当中断一直被触发时,中断线程处理函数会得不到运行,因此flag可以加上IRQF_ONESHOT。

1.2.3 释放函数 free_irq()

如果中断是共享的,则仅删除dev_id所对应的处理程序,直到最后一个中断被删除的时候才禁用此中断线。
1.2.4 中断处理函数
格式如下:

其返回类型为irqreturn_t,有如下三种:

当检测到中断正常时返回IRQ_HANDLED,否则返回IRQ_NONE,如果需要wakeup中断线程时,返回IRQ_WAKE_THERAD。
另外,中断处理程序是不用考虑重入的,当一个中断处理程序运行时,相同中断线上的中断都会被屏蔽。不过这时其他线上的中断是被打开的,这样使得更高优先级的中断能被处理。
1.3 中断上下文
和进程上下文类似,当内核在执行一个中断处理程序或者下半部时,内核处于中断上下文。在进程上下文中,进程可以通过current宏关联到当前进程,也可以睡眠,也可以调度程序。但是在内核上下文中,和进程没什么关系,和current宏也不相关,不能睡眠,不然谁又知道什么时候能唤醒如何唤醒呢?因此那些会引起休眠的函数如printk是不能放在中断上下文中的!
进程有各自的内核栈,一般为两页大小,以前中断和其共用内核栈,必须要非常节省。现在进程的内核栈缩小为一页,而中断又有了自己的中断栈(全部中断共用一页),平均比使用内核栈要大得多。但是要注意的是在内核中要尽量少使用栈,否则可能导致益处。
1.4 中断处理机制实现
设备产生中断发送电信号给中断控制器  中断控制器对应中断线激活的话把中断发给处理器  处理器跳到中断向量表定义的位置,中断号被保存在寄存器中  调到内核的do_IRQ()判断该中断线是否有注册中断处理程序,有就执行
1.5 中断控制
1.5.1 禁止本地中断
当我们需要控制中断内的数据要同步时,我们可以控制中断的禁止或者禁止内核抢占来实现。关于数据的同步,SMP的数据保护问题我们将在后面内核同步章节中讲到,这里我们只讨论如何禁止中断。
禁止和使能本地中断函数如下:

宏的实现和平台相关。注意,调用这两个函数有潜在的危险,如下例子:

local_irq_disable();
/*禁止中断时执行的代码*/
local_irq_enable();

假如在local_irq_disable()之前中断是被禁止的,之后又调用了local_irq_enable(),结果中断被enable了!
因此,我们用保存中断以前的中断状态的方法。在禁止中断之前保存中断系统的状态,然后在激活中断时,将中断恢复到原来的状态就可以了。函数如下:

这两个函数也和体系结构相关。注意,对local_irq_save()和local_irq_restore()的调用必须在同一函数中进行(flags包含中断系统的状态,不能传递给另外一个函数)。

1.5.2 禁止指定中断线
上述禁止本地CPU所有中断,有时候可能只需要禁止某条中断线就可以了。

一般使用enable_irq/disable_irq这两个函数,最好成对调用,如果调用了两次disable_irq(),那么enable_irq()两次才能enable中断。
如果是在中断处理函数中调用,请一定要使用disable_irq_nosync()函数,否则会导致死掉。
disable_irq: 当前中断处理程序执行完之后才能返回。
disable_irq_nosync: 直接关闭中断不等中断处理程序执行完就返回。
1.5.3 判断中断状态
有时我们需要判断当前是否处于中断状态,这时下面这几个宏可以派上用场了:

in_irq():判断是否在执行中断处理程序。
in_softirq():判断是否处于下半部
in_interrupt():判断是否正在执行中断处理程序或下半部
其实本质都是通过preemmpt_count来判断,看下面实现就明白了。注意preempt_count还有一部分是用来对内核是否可抢占进行计数的。

1.6 中断下半部
相对来说,下半部处理可往后推迟的事情。如网卡驱动在硬中断部分从网卡接收数据,然后在下半部对网卡数据进行处理。
有三种机制实现下半部: 软中断、tasklet和工作队列。tasklet通过软中断实现,而工作队列和它们完全不同。
a) 软中断
软中断是在编译时静态分配的,不能像tasklet那样进行动态分配。可以在所有处理器上同时执行,即使两个类型相同也可以。由softirq_action 结构表示,如下:

action()为软中断处理函数,另外还定义了一个包含32个该结构体的数组:

软中断类型列表如下:

0优先级最先运行,依次排列。注册方法如下:

其实也就是将软中断处理函数加入到softirq_vec中去。举例:
open_softirq(NET_TX_SOFTIRQ, net_tx_action);
每个被注册的软中断占据一项。
一个注册好的软中断必须要触发才能执行,通常在中断处理程序返回前标记软中断(rasie_softirq()),使其稍后被处理。待处理的软中断在以下地方被检查:
1. 从一个硬件中断代码处返回时
2. 在ksoftirqd内核线程中
3. 显示检查中
不管用什么方法,软中断最终都需要调用do_softirq()执行。

一个软中断不会抢占另一个软中断,唯一可抢占它的只有中断处理程序,因为在处理软中断处理程序的时候,中断是开启的,但它和中断处理程序一样不能休眠。本地软中断被禁止,但是其他处理器上可执行软中断,甚至是同类的中断,那么处理函数就会被执行两次,数据就会遭到破坏了。因此如果没必要,我们就用tasklet, 它的同一个处理函数不会在两个处理器上同时运行,这样也就避免了加锁的麻烦。
b) tasklet
tasklet基于软中断,虽然很相似,但接口更简单,锁保护要求也低,通常我们选择使用tasklet。只有那些执行频率很高的或者连续性要求很高的才用软中断。
Tasklet由两类软中断代表: HI_SOFTIRQ和 TASKLET_SOFTIRQ。两者区别在于前者优先级高先执行而已。Tasklet结构如下:

Next:将注册的tasklet给链接起来。
State:有三种,TASKLET_STATE_SCHED表明已被调度,准备投入运行;TASKLET_STATE_RUN表明正在运行。
Count:是tasklet引用计数器,0表示被激活,设置为挂起,这样才能运行。不为0表示被禁止,不许执行。

tasklet只有一个同一类别的tasklet会被执行,甚至是在不同处理器上,但是允许不同类别的tasklet执行。
使用tasklet之前,需要创建一个task_struct类型变量,内核提供了静态和动态两种方法。其实都一个意思,静态是内核给你分配了一个task_struct变量,而动态是要你自己分配一个task_struct变量,然后传给task_init()接口去初始化。
使用举例:

struct tasklet_struct mytasklet;
void mytasklet_handler(unsigned long data)
{printk(“this is tasklet handler!\n”);
}irqreturn_t myirq_hanlder(int irq, void *dev)
{/*do sth*/
tasklet_schedule(&mytasklet);
}
void driver_init(void param)
{tasklet_init(&mytasklet, mytasklet_handler, NULL);
}

c) work queue
工作队列相对前两者具备的优势是可以睡眠,因为它是在进程上下文工作。从本质上来说,工作队列是由专门的work thread去完成的,因此其造成的开销也是最大,因为要牵扯到内核线程甚至上下文切换。
所以,当你的工作任务是需要休眠的,那就选择work queue,否则就选择tasklet。
对于work queue的原理,可以参考另外一份文档。(work_queue_in_linux2.3.36.doc)
work_struct定义:

data:传递给函数的参数,现在的机制是直接传递真个struct work_struct,所以data 也不需要再单独传了。
entry: 连接所有工作链表,链接在一起的链表形成工作队列,work thread被唤醒时执行链表上的所有工作,执行完后就删除相应的work struct。
func: 处理函数

针对work queue,系统有两种工作方法:
1. 默认使用共享work thread来执行工作,这种情况下,每个工作任务需要尽可能快地完成任务,否则其他任务会在队列上等待而得不到执行。
2. 创建自己的工作队列并添加任务。本质上是创建了自己的work thread来执行任务。

work queue的初始化也分静态和动态两种,和tasklet一个效果。
共享工作队列使用举例:

struct work_struct my_work_struct;
void my_work_struct_handler(struct work_struct *work)
{printk(“work struct handler\n”);
}
void driver_init(void *param)
{
INIT_WORK(&my_work_struct, my_work_struct_handler);
}irqreturn_t myirq_hanlder(int irq, void *dev)
{/*do sth*/
schedule_work(&my_work_struct);

或者

schedule_delayed_work(&my_work_struct);

单独工作队列使用举例:

struct work_struct my_work_struct;
struct workqueue_struct *my_work_queue;
void my_work_struct_handler(struct work_struct *work)
{printk(“work struct handler\n”);
}
void driver_init(void *param)
{my_work_queue = create_workqueue(“my_work_queue”);
INIT_WORK(&my_work_struct, my_work_struct_handler);
}irqreturn_t myirq_hanlder(int irq, void *dev)
{/*do sth*/
queue_work(my_work_queue, &my_work_struct);
}

1.7 dts中添加中断
如果要想使用dts来获取中断号,可以参考如下例子使用方法:

其中, interrupt-parent表明参数按照msmgpio的设置, interrupts指明中断num和flags, 看了下code, flags一般都是直接code中指定。
上面这两个property是必须的。
然后你在code中获取irq gpio number,这里是通过taos,irq-gpio这个property获得。
msmgpio interrupt controller定义:

定义说明:

参考:
http://www.ibm.com/developerworks/cn/linux/l-tasklets/
《Linux内核设计与实现》
http://blog.chinaunix.net/uid-9688646-id-4052595.html
http://lwn.net/Articles/302043/
http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=21977330&id=3755609
http://www.zhihu.com/question/20055228/answer/14061827
http://blog.csdn.net/lzy_gym/article/details/7672193

                                                    Kris Fei2014/04/24

[笔记分享] [中断] 中断申请释放以及上下半部相关推荐

  1. STM32学习笔记——EXTI外部中断,光敏传感器控制LED灯

    中断系统 中断:在主程序运行过程中,当出现了特定的中断触发条件,CPU就可以暂停当前正在运行的程序去处理中断程序,并把当前程序的地址压入堆栈,中断程序处理完成后把地址弹出堆栈,回到刚刚暂停的位置继续执 ...

  2. STM32入门笔记03_EXTI外部中断详解+案例:红外对射计数、旋转编码器计数

    EXTI外部中断 中断的相关概念 中断源: 可以引起中断的事件称为中断源 中断: 在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成 ...

  3. 我的Linux(ubuntu)自学笔记分享

    ** 我的Linux(ubuntu)自学笔记分享 本人自学Linux有了好长一段时间,从最开始的无从下手.一窍不通到现在已经有很大的进步了.所以分享一下学习方法,第一最好买一本Linux书系统的学一下 ...

  4. 从结构体、内存池初始化到申请释放,详细解读鸿蒙轻内核的动态内存管理

    摘要:本文带领大家一起剖析了鸿蒙轻内核的动态内存模块的源代码,包含动态内存的结构体.动态内存池初始化.动态内存申请.释放等. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列九 动态内存Dyna ...

  5. C语言学习笔记——堆区空间申请(一)

    C语言学习笔记 堆区空间申请(一) 栈区变量分析 所有程序/软件的运行,都是由操作系统统一调配的,操作系统是程序的运行环境 运行中的多个程序之间,内存是不交叉的 程序结束后,操作系统还要释放其使用的资 ...

  6. 尚硅谷Vue2学习笔记分享

    前言 这里是尚硅谷Vue2的学习笔记分享. 原视频是尚硅谷Vue2.0+Vue3.0全套教程丨vuejs从入门到精通 Vue3的笔记链接 文章目录 前言 初识Vue 模板语法 数据绑定 el和data ...

  7. BlockChain:《区块链世界简明生存指南(一块听听)》2017-06-06 李笑来—听课笔记分享(2)

    BlockChain:<区块链世界简明生存指南(一块听听)>2017-06-06 李笑来-听课笔记分享(2) 目录 1.第1部分 2.第2部分 3.第3部分 李笑来<李笑来 Pres ...

  8. linux进程被中断打断,linux – 当中断处理程序被另一个中断中断时,中断上下文如何“恢复”?...

    我读了一些相关的帖子: You cannot sleep in an interrupt handler because interrupts do not have a backing proces ...

  9. 笔记分享②:GPS经纬度坐标转为CGCS2000

    在上一期笔记分享①:坐标定义及转换中成功解决将CGCS2000坐标系数据转为WGS1984坐标,可加载至Google Earth或者其他通用地图软件. 本期笔记总结:通过Python批量获取照片经纬度 ...

最新文章

  1. IE浏览器下ajax缓存导致数据不更新的解决方法
  2. [转]int.ToString()输出不同格式的数字字符串
  3. mscoreei.dll没有被指定在windows上运行_在Windows上使用Docker运行.NetCore
  4. ES6数组新增的几个方法
  5. struts深入原理之RequestProcessor与xml
  6. hadoop 2.6.5 + hive 集群搭建
  7. python读取print输出的内容_Python文件中将print的输出内容重定向到变量中
  8. Android AP模式下获取SSID/PASSWORD(反射机制
  9. js图片上传预览功能
  10. python爬虫学习之XPath基本语法
  11. Chapter 1 : MySQL体系结构和搜索引擎
  12. 韦东山嵌入式Linux学习——015 Nand Flash(2)-Nand Flash编程实现读地址信息
  13. linux安装微信 安装百度网盘 下载大文件
  14. torch.optim.Adam优化
  15. python发微信红包_微信现金红包 python
  16. php中mysqli_fetch_assoc( )与mysqli_fetch_row( )的区别
  17. 什么时候应该在ECMAScript 6中使用Arrow函数?
  18. WPTools改进了光标定位算法和RTF编写器
  19. android复制sim卡上的短信到手机,将数据从SIM卡轻松传输到新手机的最佳方法
  20. 自定义镜像上传阿里云

热门文章

  1. 图像特效---PS图层混合模式之明度模式
  2. MySQL卸载以及重新安装(详细,带图)
  3. [svc]二三层数据格式三层数据如何匹配路由
  4. 公布旗下(飞秋)系列软件工作原理
  5. python编写宠物类pet_除了猫狗,Python这个另类的“爬宠”你了解吗?
  6. 矩阵分析理论在实际工程中的应用_一文讲解视频智能分析技术,在文博安防工程中如何应用...
  7. 【STM32 嵌入式设计】PS2索尼游戏手柄解析和代码开发
  8. pbootcms留言提交自定义表单
  9. 告诫我自己:七成以上的创业者根本不可能突破1-20的企业规模 (转贴)
  10. excel单元格数字对比及输入条件自动筛选