中断的底半处理

RT-Thread不对中断服务例程所需要的处理时间做任何假设、限制,但如同其它实时操作系统或非实时操作系统一样,用户需要保证所有的中断服务例程在尽可能短的时间内完成(相当于中断服务例程在系统中拥有最高的优先级,会抢占所有线程优先执行)。这样在发生中断嵌套,或屏蔽了相应中断源的过程中,不会耽误了嵌套的其它中断处理过程,或自身中断源的下一次中断信号。

当一个中断发生时,中断服务例程需要取得相应的硬件状态或者数据。如果中断服务例程接下来要对状态或者数据进行简单处理,比如CPU时钟中断,中断服务例程只需对一个系统时钟tick变量进行加一操作,然后就结束中断服务例程。这类中断需要的运行时间往往都比较短。对于另外一些中断,中断服务例程在取得硬件状态或数据以后,还需要进行一系列更耗时的处理过程,通常需要将该中断分割为两部分,即上半部分(Top Half)和下半部分、底半部分(Bottom Half)。在Top Half中,取得硬件状态和数据后,打开被屏蔽的中断,给相关线程发送一条通知(可以是RT-Thread所提供的semaphore,event,mailbox或message queue等方式),然后结束中断服务例程;而接下来,相关的线程在接收到通知后,接着对状态或数据进行进一步的处理,这一过程称之为Bottom Half(底半处理)。

底半处理实现范例

在这一节中,为了详细描述Bottom Half在RT-Thread中的实现,我们以一个虚拟的网络设备接收网络数据包作为范例(例7-1a),并假设接收到数据报文后,系统对报文的分析、处理是一个相对耗时的,比外部中断源信号重要性小许多的,而且在不屏蔽中断源信号情况下也能处理的过程。

  1. /*
  2. * 程序清单:中断底半处理例子
  3. */
  4.  
  5. /* 用于唤醒线程的信号量 */
  6. rt_sem_t demo_nw_isr;
  7.  
  8. /* 数据读取、分析的线程 */
  9. void demo_nw_thread(void *param)
  10. {
  11. /* 首先对设备进行必要的初始化工作 */
  12. device_init_setting();
  13.  
  14. /* 装载中断服务例程 */
  15. rt_hw_interrupt_install(NW_IRQ_NUMBER, demo_nw_isr, RT_NULL);
  16. rt_hw_interrupt_umask(NW_IRQ_NUMBER);
  17.  
  18. /*..其他的一些操作..*/
  19.  
  20. /* 创建一个semaphore来响应Bottom Half的事件 */
  21. nw_bh_sem = rt_sem_create("bh_sem", 1, RT_IPC_FLAG_FIFO);
  22.  
  23. while(1)
  24. {
  25. /* 最后,让demo_nw_thread等待在nw_bh_sem上 */
  26. rt_sem_take(nw_bh_sem, RT_WAITING_FOREVER);
  27.  
  28. /* 接收到semaphore信号后,开始真正的Bottom Half处理过程 */
  29. nw_packet_parser (packet_buffer);
  30. nw_packet_process(packet_buffer);
  31. }
  32. }
  33.  
  34. int rt_application_init()
  35. {
  36. rt_thread_t thread;
  37.  
  38. /* 创建处理线程 */
  39. thread = rt_thread_create("nwt",
  40. demo_nw_thread, RT_NULL, 1024, 20, 5);
  41.  
  42. if (thread != RT_NULL)
  43. rt_thread_startup(thread);
  44. }

这个例子的程序创建了一个nwt线程,这个线程在启动运行后,将阻塞在nw_bh_sem信号上,一旦这个信号量被释放,将执行接下来的nw_packet_parser过程,开始Bottom Half的事件处理。接下来让我们来看一下demo_nw_isr中是如何处理Top Half,并开启Bottom Half的,如下例。

  1. void demo_nw_isr(int vector)
  2. {
  3. /* 当network设备接收到数据后,陷入中断异常,开始执行此ISR */
  4. /* 开始Top Half部分的处理,如读取硬件设备的状态以判断发生了何种中断*/
  5. nw_device_status_read();
  6.  
  7. /*..其他一些数据操作等..*/
  8.  
  9. /* 释放nw_bh_sem,发送信号给demo_nw_thread,准备开始Bottom Half */
  10. rt_sem_release(nw_bh_sem);
  11.  
  12. /* 然后退出中断的Top Half部分,结束device的ISR */
  13. }

从上面例子的两个代码片段可以看出,中断服务例程通过对一个信号量对象的等待和释放,来完成中断Bottom Half的起始和终结。由于将中断处理划分为Top和Bottom两个部分后,使得中断处理过程变为异步过程。这部分系统开销需要用户在使用RT-Thread时,必须认真考虑中断服务的处理时间是否大于给Bottom Half发送通知并处理的时间。