linux 中断

研究linux系统,不管是做驱动、协议栈还是进程调度等等,都离不开中断。这说明,要想编写正确的linux代码,不了解中断是不行的。
话说曾几何时,在大学的课堂里,老师滔滔不绝的讲解中断,说中断可以嵌套,说中断有优先级,那么linux操作系统是不是中断嵌套?是不是按优先级嵌套?
其实大家应该可以猜到了,并不完全是的。因为老师讲的是理论,linux是现实,这两者是很难相同的。就像小时候要当科学家,结果长大了发现自己天天在搬砖。
现在回到正轨上来,通过下面几个问题的讲解,大家就可以对linux的中断有个整体上的了解。
硬件平台:x86
操作系统版本:linux-2.6.24
1.什么时硬中断,什么是软中断?
硬中断:是由与系统相连的外设(比如:网卡、硬盘)自动产生的。主要是用来通知操作系统外设状态的变化。比如当网卡收到数据包的时候,就会发出一个中断。
软中断:我们知道,为了满足实时系统的要求,中断处理应该是越快越好。linux为了实现这个特点,当中断发生的时候,硬中断处理那些短时间就可以完成的工作,而将那些处理时间比较长的工作,放到中断之后来完成,也就是软中断中来完成。
2.不同的硬中断是否可以嵌套?相同的硬中断是否可以嵌套,以及是否按优先级嵌套?硬中断最多可能嵌套几级?
Linux下硬中断是可以嵌套的,但是没有优先级的概念,也就是说任何一个新的中断都可以打断正在执行的中断,但是同种中断不会打断同种中断的执行。
但是并不是所有的中断都是可以被打断的,这需要看注册的中断函数是否设置了IRQF_DISABLED,如果设置了IRQF_DISABLED,那么硬中断处理的时候是不允许被打断的,否则是允许被打断的。Peter Zijlstra在2009.3的一个讨论中关于IRQF_DISABLED的使用问题(详见http://lwn.net/Articles/321663/)。

从代码的角度上来说中断嵌套发生的位置:
硬件中断-->do_IRQ-->handle_IRQ_event-->handler。 在硬件中断到handle_event_irq之间,由于发生中断的时候CPU会自动屏蔽中断,所以在这中间是不会发生中断嵌套的,但是在handle_event_irq中,可能会重新开启中断,也就是说在handler中是可以发生中断嵌套的。
同种中断不会嵌套的实现:
linux通过一个标志位IRQ_INPROGRESS来实现。当中断类型A的一个中断A1处理的时候,linux会在do_IRQ中,handle_IRQ_event之前,置位A类型中断的IRQ_INPROGRESS位。当A1中断在handle_IRQ_event中被同种类型的中断A2到达,会调用do_IRQ,然后发现A类型中断的IRQ_INPROGRESS,就会置位IRQ_PENDING后返回,不会嵌套执行。
由于同种类型的中断不会嵌套,所以最多可能的嵌套级数,就是未设置IRQF_DISABLED中断类型的个数。(是否还有其他的限制,没有详细的研究)
3.不同的软中断是否可以嵌套?相同的软中断是否可以嵌套?
软中断的调用是通过do_softirq()来激活的。
同种类型的软中断,不可以嵌套执行。但是不同的CPU上,可以同时运行相同类型的软中断。
4.软中断在什么时间点被调度?
(1)内核显示的允许软中断的时候 local_bh_enable
(2)irq_exit()的时候
(3)ksoftirqd进程被唤醒的时候
(4)其他可能的地方(这里没有详细的追究)

Linux中断下半部处理有三种方式:软中断、tasklet、工作队列。

曾经有人问我为什么要分这几种,该怎么用。当时用书上的东西蒙混了过去,但是自己明白自己实际上是不懂的。最近有时间了,于是试着整理一下linux的中断处理机制,目的是起码从原理上能够说得通。

一、最简单的中断机制

最简单的中断机制就是像芯片手册上讲的那样,在中断向量表中填入跳转到对应处理函数的指令,然后在处理函数中实现需要的功能。类似下图:

这种方式在原来的单片机课程中常常用到,一些简单的单片机系统也是这样用。

它的好处很明显,简单,直接。

 

二、下半部

中断处理函数所作的第一件事情是什么?答案是屏蔽中断(或者是什么都不做,因为常常是如果不清除IF位,就等于屏蔽中断了),当然只屏蔽同一种中断。之所以要屏蔽中断,是因为新的中断会再次调用中断处理函数,导致原来中断处理现场的破坏。即,破坏了 interrupt context。

随着系统的不断复杂,中断处理函数要做的事情也越来越多,多到都来不及接收新的中断了。于是发生了中断丢失,这显然不行,于是产生了新的机制:分离中断接收与中断处理过程。中断接收在屏蔽中断的情况下完成;中断处理在时能中断的情况下完成,这部分被称为中断下半部。

从上图中看,只看int0的处理。Func0为中断接收函数。中断只能简单的触发func0,而func0则能做更多的事情,它与funcA之间可以使用队列等缓存机制。当又有中断发生时,func0被触发,然后发送一个中断请求到缓存队列,然后让funcA去处理。

由于func0做的事情是很简单的,所以不会影响int0的再次接收。而且在func0返回时就会使能int0,因此funcA执行时间再长也不会影响int0的接收。

 

三、软中断

下面看看linux中断处理。作为一个操作系统显然不能任由每个中断都各自为政,统一管理是必须的。

我们不可中断部分的共同部分放在函数do_IRQ中,需要添加中断处理函数时,通过request_irq实现。下半部放在do_softirq中,也就是软中断,通过open_softirq添加对应的处理函数。

 

四、tasklet

旧事物跟不上历史的发展时,总会有新事物出现。

随着中断数的不停增加,软中断不够用了,于是下半部又做了进化。

软中断用轮询的方式处理。假如正好是最后一种中断,则必须循环完所有的中断类型,才能最终执行对应的处理函数。显然当年开发人员为了保证轮询的效率,于是限制中断个数为32个。

为了提高中断处理数量,顺道改进处理效率,于是产生了tasklet机制。

Tasklet采用无差别的队列机制,有中断时才执行,免去了循环查表之苦。

CDMA因为频谱重叠问题,必须降功率。而功率降低后,又发现原来功率低了有助于环保。

Tasklet作为一种新机制,显然可以承担更多的优点。正好这时候SMP越来越火了,因此又在tasklet中加入了SMP机制,保证同种中断只能在一个cpu上执行。在软中断时代,显然没有这种考虑。因此同一种中断可以在两个cpu上同时执行,很可能造成冲突。

总结下tasklet的优点:

(1)无类型数量限制;

(2)效率高,无需循环查表;

(3)支持SMP机制;

 

五、工作队列

前面的机制不论如何折腾,有一点是不会变的。它们都在中断上下文中。什么意思?说明它们不可挂起。而且由于是串行执行,因此只要有一个处理时间较长,则会导致其他中断响应的延迟。为了完成这些不可能完成的任务,于是出现了工作队列。工作队列说白了就是一组内核线程,作为中断守护线程来使用。多个中断可以放在一个线程中,也可以每个中断分配一个线程。

工作队列对线程作了封装,使用起来更方便。

因为工作队列是线程,所以我们可以使用所有可以在线程中使用的方法。

Tasklet其实也不一定是在中断上下文中执行,它也有可能在线程中执行。

假如中断数量很多,而且这些中断都是自启动型的(中断处理函数会导致新的中断产生),则有可能cpu一直在这里执行中断处理函数,会导致用户进程永远得不到调度时间。

为了避免这种情况,linux发现中断数量过多时,会把多余的中断处理放到一个单独的线程中去做,就是ksoftirqd线程。这样又保证了中断不多时的响应速度,又保证了中断过多时不会把用户进程饿死。

问题是我们不能保证我们的tasklet或软中断处理函数一定会在线程中执行,所以还是不能使用进程才能用的一些方法,如放弃调度、长延时等。

 

六、使用方式总结

Request_irq挂的中断函数要尽量简单,只做必须在屏蔽中断情况下要做的事情。

中断的其他部分都在下半部中完成。

软中断的使用原则很简单,永远不用。它甚至都不算是一种正是的中断处理机制,而只是tasklet的实现基础。

工作队列也要少用,如果不是必须要用到线程才能用的某些机制,就不要使用工作队列。其实对于中断来说,只是对中断进行简单的处理,大部分工作是在驱动程序中完成的。所以有什么必要非使用工作队列呢?

除了上述情况,就要使用tasklet。

即使是下半部,也只是作必须在中断中要做的事情,如保存数据等,其他都交给驱动程序去做。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值