中断与轮询
当驱动外设工作时,其编程模式到底采用中断模式触发还是轮训模式触发往往是驱动开发人员首先需要考虑的问题,并且这个问题在实时操作系统与分时操作系统中差异非常大。
轮询模式本身采用顺序执行的方式:查询到相应的事件然后进行对应的处理。所以轮训模式从实现上来说,相对简单清晰。
例如往串口中写入数据,仅当串口控制器写完一个数据时,程序代码才写入下一个数据(否则这个数据丢弃掉)。
//轮询模式向串口写入数据
while(size)
{
while(!(uart->uart_device->SR & USART_FLAG_TXE));
uart->uart_device->DR = (*ptr & 0x1FF);
++ptr;
--size;
}
在实时系统中轮询模式可能会出现非常大的问题,因为在实时操作系统中,当一个程序持续地执行时(轮询时),它所有的线程会一直运行,比它优先级低的线程都不会得到运行。
而分时系统中,这点恰恰相反,几乎没有优先级之分,可以在一个时间片运行这个程序,然后在另外一段时间片上运行另外一段程序。
所以通常情况下,实时系统中更多采用的是中断模式来驱动外设。
当数据达到时,由中断唤醒相关的处理线程,再继续进行后续的动作。
例如一些携带FIFO的串口外设。
线程先向串口的FIFO中写入数据,当FIFO满时,线程主动挂起。
串口控制器持续地从FIFO中取出数据并配置的波特率发送出去。当FIFO中所有数据都发送完成时,向处理器触发一个中断;中断服务程序得到执行,唤醒这个线程。
对于低俗设备来说,运用这种模式非常好,因为在串口外设把FIFO中的数据发送出去前,处理器可以运行其它的线程,这样就提高了系统的整体运行效率。
但是对于高速设备,例如传输速度达到10Mbps的时候,假设一次发送的数据量是32字节,发送一段时间25us。当数据需要持续传输时,系统将在25us后触发一个中断唤醒上层线程继续下次传递。
假设系统的线程切换时间是 8us(通常实时操作系统的线程上下文切换时间只有几个 us),那么当整个系统运行时,对于数据带宽利用率将只有 25/(25+8) =75.8%。但是采用轮询模式,数据带宽的利用率则可能达到 100%。这个也是大家普遍认为实时系统中数据吞吐量不足的缘故,系统开销消耗在了线程切换上(有些实时系统甚至会如本章前面说的,采用底半处理,分级的中断处理方式,相当于又拉长中断到发送线程的时间开销,效率会更进一步下降)。
发送数据量越小,发送速度越快,对于数据吞吐量的影响越大。
- 增加每次数据量发送的长度,每次尽量让外设尽量多地发送数据。
- 必要情况下更改中断模式为轮询模式。同时为了解决轮询方式一直抢占处理机,其它低优先级线程得不到运行的情况,可以把轮询线程的优先级适当降低。