Linux中断上半部和下半部
前言
cpu在执行程序时,如果有外部中断触发时,如定时器中断、串行总线中断等,cpu停止当前任务从而转去响应中断处理。对于中断函数的处理,原则是尽快处理完事务并退出中断,这一点也比较好理解,尽快处理中断并返回,保证正常任务的执行,并q且能否响应其他事务的中断,保证实时性和并发性。
其实,在单片机逻辑编程中已经使用过Linux中断“上下部分”的思维,或者说,Linux中断“上下半部”设计者灵感即源自最初的裸机编程。例如,在单片机裸机编程中,需要通过串口(UART)进行数据通信,对于接收数据情况下,在采用中断方式接收时,我们一般会先将数据存放在一个缓存(buf)中,中断函数只负责将数据存入缓存,而在主函数中获取缓存数据并处理。在这个过程中,中断处理部分即将数据存入缓存的动作为“中断上半部”,当然还包括从寄存器获取数据,改变寄存器状态等。“中断下半部”则是获取缓存数据并处理。
RTOS中断处理
从裸机编程上升到系统层面,单片机运行实时系统(RTOS)。RTOS一般以线程为调度任务,没有进程概念,但都会提供一套类似标准操作系统的 线程同步机制。此时,在设计中断程序时,可以更好实现“上半部”和“下半部”,特别是“下半部”。以RT-Thread为例,实现一个串口中断接收函数。上半部分负责将数据放入缓存,并通过信号量通知下半部分处理。下半部实现,可以创建一个处理线程,当获得上半部信号量时则调度线程进行处理数据,否则则挂起该线程,节约cpu资源。
Linux中断
不同于裸机编程,操作系统是多个进程和多个线程执行,宏观上达到并行运行的状态,外设中断则会打断内核中任务调度和运行,及屏蔽其外设的中断响应,如果中断函数耗时过长则使得系统实时性和并发性降低。中断原则是尽可能处理少的事务,而一些设备中往往需要处理大量的耗时事务。为了提高系统的实时性和并发性,Linux内核将中断处理程序分为上半部(top half)和下半部(bottom half)。上半部分任务比较少,处理一些寄存器操作、时间敏感任务,以及“登记中断”通知内核及时处理下半部的任务。下半部分,则负责处理中断任务中的大部分工作,如一个总线通信系统中数据处理部分。
Linux中断上下部分区别:
1)上半部由外设中断触发,下半部由上半部触发。
2)上半部不会被其他中断打断,下半部是可以被打断的。
3)上半部分处理任务要快,主要任务、耗时任务放在下半部。
一般来说,在中断上半部执行完毕,下半部即在内核的调度下被执行,当然如果有其他更高优先级需处理的任务,会先处理该任务再调度处理下半部,或者在系统空闲时间进行处理。
Linux中断设计
对于Linux系统设备而言,一个完整的中断程序由上半部和下半部分共同构成,在编写设备驱动程序前,就需考虑好上半部和下半部的分配。很多时候上半部与下半部并没有严格的区分界限,主要由程序员根据实际设计,如某些外设中断可以没有下半部。关于上下半部的划分原则,就是主要事务、耗时事务划分在下半部处理。
关于中断上下部分的设计,可以参考以下原则:
1)与硬件相关的操作,如操作寄存器,必须放在上半部。
2)对时间敏感、要求实时性的任务放在上半部。
3)该任务不能被其他中断或者进程打断的放在上半部。
4)实时性要求不高的任务、耗时任务放在下半部。
Linux中断下半部实现
Linux下半部实现,通过内核版本更新中实现下衍化,最原始的实现手段是BH(bottom half)后在2.5版本移除,在2.3版本引入软中断(softirq)和tasklet机制,在2.5版本引入工作队列(work queue在)。因此,目前使用方式是三种,软中断、tasklet机制、工作队列。