简介

在使用二值信号量的时候会遇到很常见的一个问题——优先级翻转,优先级翻转在可剥夺内核中是非常常见的,在实时系统中不允许出现这种现象,这样会破坏任务的预期顺序,可能会导致严重的后果。以下就是一个优先级翻转的例子。
15.优先级反转 - 图1

  • 任务 H 和任务 M 处于挂起状态,等待某一事件的发生,任务 L 正在运行。
  • 某一时刻任务 L 想要访问共享资源,在此之前它必须先获得对应该资源的信号量。
  • 任务 L 获得信号量并开始使用该共享资源。
  • 由于任务 H 优先级高,它等待的事件发生后便剥夺了任务 L 的 CPU 使用权。
  • 任务 H 开始运行。
  • 任务 H 运行过程中也要使用任务 L 正在使用着的资源,由于该资源的信号量还被任务L 占用着,任务 H 只能进入挂起状态,等待任务 L 释放该信号量。
  • 任务 L 继续运行。
  • 由于任务 M 的优先级高于任务 L,当任务 M 等待的事件发生后,任务 M 剥夺了任务L 的 CPU 使用权。
  • 任务 M 处理该处理的事。
  • 任务 M 执行完毕后,将 CPU 使用权归还给任务 L。
  • 任务 L 继续运行。
  • 最终任务 L 完成所有的工作并释放了信号量,到此为止,由于实时内核知道有个高优先级的任务在等待这个信号量,故内核做任务切换。
  • 任务 H 得到该信号量并接着运行。

在这种情况下,任务 H 的优先级实际上降到了任务 L 的优先级水平。因为任务 H 要一直等待直到任务 L 释放其占用的那个共享资源。由于任务 M 剥夺了任务 L 的 CPU 使用权,使得任务 H 的情况更加恶化,这样就相当于任务 M 的优先级高于任务 H,导致优先级翻转。

FreeRTOS的互斥信号量处理优先级反转

FreeRTOS中xTaskPriorityDisinherit() 是处理优先级继承的函数。

  1. BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder )
  2. {
  3. TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;
  4. BaseType_t xReturn = pdFALSE;
  5. //函数的参数 pxMutexHolder 表示拥有此互斥信号量任务控制块,所以要先判断此互斥信号量是否已经被其他任务获取。
  6. if( pxMutexHolder != NULL )
  7. {
  8. //当一个任务获取到互斥信号量以后就会涉及到优先级继承的问题,正在释放互斥
  9. //信号量的任务肯定是当前正在运行的任务 pxCurrentTCB。
  10. configASSERT( pxTCB == pxCurrentTCB );
  11. configASSERT( pxTCB->uxMutexesHeld );
  12. /*
  13. * 有的任务可能会获取多个互斥信号量,所以就需要标记任务当前获取到的互斥信号量
  14. * 个数,任务控制块结构体的成员变量 uxMutexesHeld 用来保存当前任务获取到的互斥信号量个
  15. * 数。任务每释放一次互斥信号量,变量 uxMutexesHeld 肯定就要减一。
  16. */
  17. ( pxTCB->uxMutexesHeld )--;
  18. //是否存在优先级继承?如果存在的话任务当前优先级肯定和任务基优先级不同。
  19. if( pxTCB->uxPriority != pxTCB->uxBasePriority )
  20. {
  21. /*
  22. * 判断当前释放的是不是任务所获取到的最后一个互斥信号量,因为如果任务还获取了
  23. * 其他互斥信号量的话就不能处理优先级继承。优先级继承的处理必须是在释放最后一个互斥信
  24. * 号量的时候。
  25. */
  26. if( pxTCB->uxMutexesHeld == ( UBaseType_t ) 0 )
  27. {
  28. /*
  29. * 优先级继承的处理说白了就是将任务的当前优先级降低到任务的基优先级,所以要把
  30. * 当前任务先从任务就绪表中移除。当任务优先级恢复为原来的优先级以后再重新加入到就绪表中。
  31. */
  32. if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
  33. {
  34. //如果任务继承来的这个优先级对应的就绪表中没有其他任务的话就将取消这个优先级的就绪态。
  35. taskRESET_READY_PRIORITY( pxTCB->uxPriority );
  36. }
  37. else
  38. {
  39. mtCOVERAGE_TEST_MARKER();
  40. }
  41. //使用新的优先级将任务重新添加到就绪列表中
  42. traceTASK_PRIORITY_DISINHERIT( pxTCB, pxTCB->uxBasePriority );
  43. //重新设置任务的优先级为任务的基优先级 uxBasePriority
  44. pxTCB->uxPriority = pxTCB->uxBasePriority;
  45. /* Reset the event list item value. It cannot be in use for
  46. any other purpose if this task is running, and it must be
  47. running to give back the mutex. */
  48. //复位任务的事件列表项
  49. listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxTCB->uxPriority );
  50. //将优先级恢复后的任务重新添加到任务就绪表中
  51. prvAddTaskToReadyList( pxTCB );
  52. //返回 pdTRUE,表示需要进行任务调度
  53. xReturn = pdTRUE;
  54. }
  55. else
  56. {
  57. mtCOVERAGE_TEST_MARKER();
  58. }
  59. }
  60. else
  61. {
  62. mtCOVERAGE_TEST_MARKER();
  63. }
  64. }
  65. else
  66. {
  67. mtCOVERAGE_TEST_MARKER();
  68. }
  69. return xReturn;
  70. }