首先需要了解一下中断的概念:为了提高CPU和外围硬件(硬盘,键盘,鼠标等等)之间协同工作的性能,引入了中断的机制。
没有中断的话,CPU和外围设备之间协同工作可能只有轮询这个方法:CPU定期检查硬件状态,需要处理时就处理,否则就跳过。
当硬件忙碌的时候,CPU很可能会做许多无用功(每次轮询都是跳过不处理)。中断机制是硬件在需要的时候向CPU发出信号,CPU暂时停止正在进行的工作,来处理硬件请求的一种机制。
一个“中断”仅仅是一个信号,当硬件需要获得处理器对它的关注时,就可以发送这个信号。内核维护了一个中断信号线的注册表,该注册表类似于I/O端口的注册表。
RTOS: RTOS一般以线程为调度任务,没有进程概念,但都会提供一套类似标准操作系统的 线程同步机制。此时,在设计中断程序时,可以更好实现“上半部”和“下半部”,特别是“下半部”。以RT-Thread为例,实现一个串口中断接收函数。上半部分负责将数据放入缓存,并通过信号量通知下半部分处理。下半部实现,可以创建一个处理线程,当获得上半部信号量时则调度线程进行处理数据,否则则挂起该线程,节约cpu资源。
Linux: 操作系统是多个进程和多个线程执行,宏观上达到并行运行的状态,外设中断则会打断内核中任务调度和运行,及屏蔽其外设的中断响应,如果中断函数耗时过长则使得系统实时性和并发性降低。中断原则是尽可能处理少的事务,而一些设备中往往需要处理大量的耗时事务。为了提高系统的实时性和并发性,Linux内核将中断处理程序分为上半部(top half)和下半部(bottom half)。上半部分任务比较少,处理一些寄存器操作、时间敏感任务,以及“登记中断”通知内核及时处理下半部的任务。下半部分,则负责处理中断任务中的大部分工作,如一个总线通信系统中数据处理部分。
中断一般分为异步中断(一般由硬件引起)和同步中断(一般由处理器本身引起)。
** 异步中断:** CPU处理中断的时间过长,所以先将硬件复位,使硬件可以继续自己的工作,然后在适当时候处理中断请求中耗时的部分。
** 同步中断:** CPU处理完中断请求的所有工作后才反馈硬件。
对于中断处理例程来讲,它的一个典型的任务就是:如果中断通知进程所等待的事件已经发生,比如新的数据到达就会唤醒在该设备上休眠的进程。无论是快速还是慢速处理例程,程序员都应该编写执行事件尽可能短的处理例程。如果需要执行一个长时间的计算任务,做好的办法就是使用上下半部处理机制,以便让工作在更安全的时间里调度计算任务。
中断处理是分为两个部分:中断处理程序是上半部,它接收到一个中断,就立即执行,但只做有严格时限的工作;而另外被叫做下半部的另外一个部分主要做被允许能稍后完成的工作。
中断上半部:
上半部的功能是响应中断。当中断发生时,它就把设备驱动程序中中断处理例程的下半部挂到设备的下半部执行队列中去,然后继续等待新的中断到来。这样一来,上半部的执行速度就会很快,它就可以接受更多它负责的设备所产生的中断了。上半部之所以快,是因为它是完全屏蔽中断的,如果它没有执行完,其他中断就不能及时地处理,只能等到这个中断处理程序执行完毕以后。所以要尽可能多的对设备产生的中断进行服务和处理,中断处理程序就一定要快。
中断下半部:
下半部的功能是处理比较复杂的过程。下半部和上半部最大的区别是可中断,而上半部却不可中断。下半部几乎完成了中断处理程序所有的事情,因为上半部只是将下半部排到了它们所负责的设备中断的处理队列中去,然后就不做其它的处理了。下半部所负责的工作一般是查看设备以获得产生中断的事件信息,并根据这些信息(一般通过读设备上的寄存器得来)进行相应的处理。下半部是可中断的,所以在运行期间,如果其它设备产生了中断,这个下半部可以暂时的中断掉,等到那个设备的上半部运行完了,再回头运行这个下半部。
对于软中断,内核会选择几个特殊的实际进行处理(常见的是中断处理程序返回时)。软中断被触发的频率有时会很好,而且还可能会自行重复触发,这带来的结果就是用户空间的进程无法获得足够的处理器时间,因为处于饥饿状态。同时,如果单纯的对重复触发的软中断采取不立即处理的策略也是无法接受的。两种极端但完美的情况是什么样的呢:
1.只要还有被触发并等待处理的软中断,本次执行就要负责处理,重新触发的软中断也在本次执行返回前被处理。问题在于,用户进程可能被忽略而使其处于饥饿状态。
2.选择不处理重新触发的软中断。在从中断返回的时候,内核和平常一样,也会检查所有挂起的软中断并处理它们,但是,任何自行重新触发的软中断都不会马上处理,它们被放到下一个软中断执行时机去处理。问题在于新的或重新触发的软中断必要要等一定的时间才能被执行。
tasklet是通过软中断实现的,所以它们本身也是软中断。它由两类软中断代表:HI_SOFTIRQ和TASKLET_SOFTIRQ.区别在于前者会先于后者执行。Tasklets由tasklet_struct结构表示,每个结构体单独代表一个tasklet,在linux/interrupt.h中定义:在中断处理程序中触发软中断是最常见的形式,中断处理程序执行硬件设备的相关操作,然后触发相应的软中断,最后退出。内核在执行完中断处理程序后,马上就会调用do_softirq()函数。于是,软中断就开始执行中断处理程序留给它去完成的剩下任务。
工作队列子系统是一个用于创建内核线程的接口,通过它可以创建一个工作者线程来专门处理中断的下半部工作。
工作队列和tasklet不一样,不是基于软中断来实现的。其静态定义了一个工作,动态定义了一个工作。
静态定义的工作由系统工作队列(events/n)调度,