uC/OS中锁调度器为什么比关中断效率更高

1. 背景

今天看《μC/OS-III源码分析笔记》的时候学习了通过使用锁调度器代替关中断,并使用中断延迟提交,能够大大节省关中断的时间,提高运行效率。但是效率具体是如何提高的,书中的说明我没有太看明白,于是我就把书中提到的函数的源代码找出来分析了一下,希望能够理解锁调度器关中断两者之间的区别。uC/OS完整源代码可以从官网我的Github上下载。

2. 优化原因

使用关中断进入临界区以后,系统不会响应中断请求,如果临界区里需要做一些如I/O、与外设交互等需要进入较长时间的等待状态的任务,此时CPU就处于忙等状态,不能够响应中断去运行中断处理程序,这就降低了系统的运行效率。

锁调度器的方式可以保证当前运行的任务不会被其他任务抢占,使任务进入了临界区。

但是锁住调度器之后还可以响应中断,减少了关中断的时间,使得CPU不会长时间处于忙等状态,提高了系统的实时性和运行效率。

3. 关中断和锁调度器

3.1. 概念

  • 中断:中断机制是处理器的重要基础设施,用来应对各种事件的响应和处理。当外设或者处理器自身有事件发生时,处理器会暂停执行当前的代码,并转向处理这些中断事务。中断可分为外部中断、内部中断和软件中断1

  • 抢占:通过调度器实现,抢占的发生可能是因为有更高优先级的任务进入就绪队列(优先级抢占机制)或该任务的时间片已结束(时间片轮转机制)。抢占可分为用户抢占和内核抢占。

  • 关中断:进入临界段,禁止其他中断操作,关中断以后产生的中断请求会被挂起,等待开中断以后再响应。

  • 开中断:退出临界段,允许中断操作。

  • 锁调度器:锁住调度器(Lock the scheduler),不允许其他任务抢占当前任务。

  • 开启调度器:解锁调度器(Unlock the scheduler),允许其他任务抢占当前任务。

  • RTOS:实时操作系统(Real Time Operating System)。

  • ISR:中断服务程序(Interrupt Service Routine)。在CPU保护中断现场之后,RTOS进入,进行中断预处理,记录当前中断嵌套深度,查找用户登记的中断服务程序,然后CPU开始执行中断服务程序1

3.2. 源代码

3.2.1. 开/关中断

这里有两种方式通过关中断进入临界段

  • CPU_INT_DIS()
  • CPU_CRITICAL_ENTER()

CPU_CFG_INT_DIS_MEAS_EN标记为False,两个函数完全一致,后者只是将前者封装了一下。

CPU_CFG_INT_DIS_MEAS_EN标记为True,CPU_CRITICAL_ENTER()会记录关中断的时间。


#define CPU_INT_DIS() do { CPU_IntDis(); } while (0)
/* Disable interrupts. */
#define CPU_INT_EN() do { CPU_IntEn(); } while (0)
/* Enable interrupts.*/

#ifdef CPU_CFG_INT_DIS_MEAS_EN
/* Disable interrupts, ... */
/* & start interrupts disabled time measurement.*/
#define CPU_CRITICAL_ENTER() do { CPU_INT_DIS(); \ CPU_IntDisMeasStart(); } while (0)
/* Stop & measure interrupts disabled time, */
/* ... & re-enable interrupts. */
#define CPU_CRITICAL_EXIT() do { CPU_IntDisMeasStop(); \ CPU_INT_EN(); } while (0)
/* -------------------------------------------- */
#else

#define CPU_CRITICAL_ENTER() do { CPU_INT_DIS(); } while (0)
/* Disable interrupts.*/
#define CPU_CRITICAL_EXIT() do { CPU_INT_EN(); } while (0)
/* Re-enable interrupts.*/
#endif

3.2.2. 锁调度器

  • OS_CFG_ISR_POST_DEFERRED_EN:标记是否使用中断延迟提交
// os_cfg.h
#define OS_CFG_ISR_POST_DEFERRED_EN 0u
/* Enable (1) or Disable (0) Deferred ISR posts*/

这里有两种方式进入临界段

  1. 调用OS_CRITICAL_ENTER()锁调度器。
  2. 先调用CPU_CRITICAL_ENTER()关中断,再调用OS_CRITICAL_ENTER_CPU_EXIT()锁调度器并开中断。
  • OS_CFG_ISR_POST_DEFERRED_EN标记为False,OS_CRITICAL_ENTER()实现的是关中断,OS_CRITICAL_ENTER_CPU_EXIT()是一个空函数。

  • CPU_CFG_INT_DIS_MEAS_EN标记为True,这里的两个函数都能实现锁调度器的功能。


// os.h
#if (OS_CFG_ISR_POST_DEFERRED_EN == DEF_ENABLED)
/* Deferred ISR Posts */
/* Lock the scheduler*/
#define OS_CRITICAL_ENTER() \ do { \ CPU_CRITICAL_ENTER(); \ OSSchedLockNestingCtr++; \ if (OSSchedLockNestingCtr == 1u) { \ OS_SCHED_LOCK_TIME_MEAS_START(); \ } \ CPU_CRITICAL_EXIT(); \ } while (0)
/* Lock the scheduler but re-enable interrupts */
#define OS_CRITICAL_ENTER_CPU_EXIT() \ do { \ OSSchedLockNestingCtr++; \ \ if (OSSchedLockNestingCtr == 1u) { \ OS_SCHED_LOCK_TIME_MEAS_START(); \ } \ CPU_CRITICAL_EXIT(); \ } while (0)

/* Scheduling occurs only if an interrupt occurs*/
#define OS_CRITICAL_EXIT() \ do { \ CPU_CRITICAL_ENTER(); \ OSSchedLockNestingCtr--; \ if (OSSchedLockNestingCtr == (OS_NESTING_CTR)0) { \ OS_SCHED_LOCK_TIME_MEAS_STOP(); \ if (OSIntQNbrEntries > (OS_OBJ_QTY)0) { \ CPU_CRITICAL_EXIT(); \ OS_Sched0(); \ } else { \ CPU_CRITICAL_EXIT(); \ } \ } else { \ CPU_CRITICAL_EXIT(); \ } \ } while (0)

#define OS_CRITICAL_EXIT_NO_SCHED() \ do { \ CPU_CRITICAL_ENTER(); \ OSSchedLockNestingCtr--; \ if (OSSchedLockNestingCtr == (OS_NESTING_CTR)0) { \ OS_SCHED_LOCK_TIME_MEAS_STOP(); \ } \ CPU_CRITICAL_EXIT(); \ } while (0)

#else
/* Direct ISR Posts */

#define OS_CRITICAL_ENTER() CPU_CRITICAL_ENTER()

#define OS_CRITICAL_ENTER_CPU_EXIT()

#define OS_CRITICAL_EXIT() CPU_CRITICAL_EXIT()

#define OS_CRITICAL_EXIT_NO_SCHED() CPU_CRITICAL_EXIT()

#endif

4. 参考文献

[1] 刘旭明. 嵌入式实时操作系统原理与最佳实践. 机械工业出版社, 2014.

联系邮箱:curren_wong@163.com

Github:https://github.com/CurrenWong

欢迎转载/Star/Fork,有问题欢迎通过邮箱交流。