找回密码
 立即注册
首页 业界区 安全 freeRTOS源码解析4--tasks.c 3

freeRTOS源码解析4--tasks.c 3

蓝娅萍 2025-6-8 13:01:37
4.2.6 任务删除--vTaskDelete

这个接口并不复杂,主要是在判断是否要放到xTasksWaitingTermination列表里,还是直接处理。
1.gif
2.gif
  1.   1 void vTaskDelete( TaskHandle_t xTaskToDelete )
  2.   2 {
  3.   3     TCB_t * pxTCB;
  4.   4     BaseType_t xDeleteTCBInIdleTask = pdFALSE;
  5.   5     BaseType_t xTaskIsRunningOrYielding;
  6.   6
  7.   7     taskENTER_CRITICAL();
  8.   8     {
  9.   9         /* If null is passed in here then it is the calling task that is
  10. 10          * being deleted. */
  11. 11         /* 如果是NULL就是删除自己,否则是删除其他任务 */
  12. 12         pxTCB = prvGetTCBFromHandle( xTaskToDelete );
  13. 13
  14. 14         /* Remove task from the ready/delayed list. */
  15. 15         if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
  16. 16         {
  17. 17             taskRESET_READY_PRIORITY( pxTCB->uxPriority );
  18. 18         }
  19. 19         else
  20. 20         {
  21. 21             mtCOVERAGE_TEST_MARKER();
  22. 22         }
  23. 23
  24. 24         /* Is the task waiting on an event also? */
  25. 25         if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
  26. 26         {
  27. 27             ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
  28. 28         }
  29. 29         else
  30. 30         {
  31. 31             mtCOVERAGE_TEST_MARKER();
  32. 32         }
  33. 33
  34. 34         /* Increment the uxTaskNumber also so kernel aware debuggers can
  35. 35          * detect that the task lists need re-generating.  This is done before
  36. 36          * portPRE_TASK_DELETE_HOOK() as in the Windows port that macro will
  37. 37          * not return. */
  38. 38         /* 这个变量是用于TRACE相关的,调试用的,先不管,以后看trace的代码再解析 */
  39. 39         uxTaskNumber++;
  40. 40
  41. 41         /* Use temp variable as distinct sequence points for reading volatile
  42. 42          * variables prior to a logical operator to ensure compliance with
  43. 43          * MISRA C 2012 Rule 13.5. */
  44. 44         /* 确定是否正在运行或正在调度到这个任务 */
  45. 45         xTaskIsRunningOrYielding = taskTASK_IS_RUNNING_OR_SCHEDULED_TO_YIELD( pxTCB );
  46. 46
  47. 47         /* If the task is running (or yielding), we must add it to the
  48. 48          * termination list so that an idle task can delete it when it is
  49. 49          * no longer running. */
  50. 50         if( ( xSchedulerRunning != pdFALSE ) && ( xTaskIsRunningOrYielding != pdFALSE ) )
  51. 51         {
  52. 52             /* A running task or a task which is scheduled to yield is being
  53. 53              * deleted. This cannot complete when the task is still running
  54. 54              * on a core, as a context switch to another task is required.
  55. 55              * Place the task in the termination list. The idle task will check
  56. 56              * the termination list and free up any memory allocated by the
  57. 57              * scheduler for the TCB and stack of the deleted task. */
  58. 58             /* 看注释,不放到终止列表中,无法删除。需要依赖空闲任务来处理
  59. 59              * 具体原因暂时还不清楚,看来需要阅读更多源码才能明白。 */
  60. 60             vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );
  61. 61
  62. 62             /* Increment the ucTasksDeleted variable so the idle task knows
  63. 63              * there is a task that has been deleted and that it should therefore
  64. 64              * check the xTasksWaitingTermination list. */
  65. 65             ++uxDeletedTasksWaitingCleanUp;
  66. 66
  67. 67             /* Delete the task TCB in idle task. */
  68. 68             xDeleteTCBInIdleTask = pdTRUE;
  69. 69
  70. 70             /* The pre-delete hook is primarily for the Windows simulator,
  71. 71              * in which Windows specific clean up operations are performed,
  72. 72              * after which it is not possible to yield away from this task -
  73. 73              * hence xYieldPending is used to latch that a context switch is
  74. 74              * required. */
  75. 75             /* 只有在Windows下才有用,先不分析了 */
  76. 76             #if ( configNUMBER_OF_CORES == 1 )
  77. 77                 portPRE_TASK_DELETE_HOOK( pxTCB, &( xYieldPendings[ 0 ] ) );
  78. 78             #endif
  79. 79         }
  80. 80         else
  81. 81         {
  82. 82             /* 这里是删除阻塞任务(pxDelayedTaskList)的(xTaskIsRunningOrYielding = pdFALSE) */
  83. 83             --uxCurrentNumberOfTasks;
  84. 84
  85. 85             /* Reset the next expected unblock time in case it referred to
  86. 86              * the task that has just been deleted. */
  87. 87             /* 主要用于更新最久阻塞的剩余阻塞时间,防止最近需要运行的就是删除的这个任务 */
  88. 88             prvResetNextTaskUnblockTime();
  89. 89         }
  90. 90     }
  91. 91     taskEXIT_CRITICAL();
  92. 92
  93. 93     /* If the task is not deleting itself, call prvDeleteTCB from outside of
  94. 94      * critical section. If a task deletes itself, prvDeleteTCB is called
  95. 95      * from prvCheckTasksWaitingTermination which is called from Idle task. */
  96. 96     /* 如果删除的是当前运行的任务在空闲任务中处理,阻塞任务在这里处理 */
  97. 97     if( xDeleteTCBInIdleTask != pdTRUE )
  98. 98     {
  99. 99         prvDeleteTCB( pxTCB );
  100. 100     }
  101. 101
  102. 102     /* Force a reschedule if it is the currently running task that has just
  103. 103      * been deleted. */
  104. 104     /* 如果删除的是当前运行的任务,需要调度,这很明显。 */
  105. 105     #if ( configNUMBER_OF_CORES == 1 )
  106. 106     {
  107. 107         if( xSchedulerRunning != pdFALSE )
  108. 108         {
  109. 109             if( pxTCB == pxCurrentTCB )
  110. 110             {
  111. 111                 configASSERT( uxSchedulerSuspended == 0 );
  112. 112                 taskYIELD_WITHIN_API();
  113. 113             }
  114. 114             else
  115. 115             {
  116. 116                 mtCOVERAGE_TEST_MARKER();
  117. 117             }
  118. 118         }
  119. 119     }
  120. 120     #endif /* #if ( configNUMBER_OF_CORES == 1 ) */
  121. 121 }
复制代码
vTaskDelete 
3.gif
4.gif
  1. 1 static void prvDeleteTCB( TCB_t * pxTCB )
  2. 2 {
  3. 3     #if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( portUSING_MPU_WRAPPERS == 0 ) )
  4. 4     {
  5. 5         /* The task can only have been allocated dynamically - free both
  6. 6          * the stack and TCB. */
  7. 7         vPortFreeStack( pxTCB->pxStack );
  8. 8         vPortFree( pxTCB );
  9. 9     }
  10. 10     #elif ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
  11. 11     {
  12. 12         /* The task could have been allocated statically or dynamically, so
  13. 13          * check what was statically allocated before trying to free the
  14. 14          * memory. */
  15. 15         /* 按需释放tcb和stack */
  16. 16         if( pxTCB->ucStaticallyAllocated == tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB )
  17. 17         {
  18. 18             /* Both the stack and TCB were allocated dynamically, so both
  19. 19              * must be freed. */
  20. 20             vPortFreeStack( pxTCB->pxStack );
  21. 21             vPortFree( pxTCB );
  22. 22         }
  23. 23         else if( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_ONLY )
  24. 24         {
  25. 25             /* Only the stack was statically allocated, so the TCB is the
  26. 26              * only memory that must be freed. */
  27. 27             vPortFree( pxTCB );
  28. 28         }
  29. 29         else
  30. 30         {
  31. 31             /* Neither the stack nor the TCB were allocated dynamically, so
  32. 32              * nothing needs to be freed. */
  33. 33             configASSERT( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB );
  34. 34             mtCOVERAGE_TEST_MARKER();
  35. 35         }
  36. 36     }
  37. 37     #endif /* configSUPPORT_DYNAMIC_ALLOCATION */
  38. 38 }
复制代码
prvDeleteTCB 4.2.7 获取任务状态--eTaskGetState

这个接口可以大致看出任务在哪些情况下会处于什么状态。
首先是返回值的说明:
  1. 1 typedef enum
  2. 2 {
  3. 3     eRunning = 0, /* 运行态. */
  4. 4     eReady,       /* 就绪态, 任务在 ready or pending ready 列表. */
  5. 5     eBlocked,     /* 阻塞态. */
  6. 6     eSuspended,   /* 挂起态, 任务在 挂起列表, 或处于无限延迟的阻塞中. */
  7. 7     eDeleted,     /* 等待删除. */
  8. 8     eInvalid      /* Used as an 'invalid state' value. */
  9. 9 } eTaskState;
复制代码
5.gif
6.gif
  1.   1 eTaskState eTaskGetState( TaskHandle_t xTask )
  2.   2 {
  3.   3     eTaskState eReturn;
  4.   4     List_t const * pxStateList;
  5.   5     List_t const * pxEventList;
  6.   6     List_t const * pxDelayedList;
  7.   7     List_t const * pxOverflowedDelayedList;
  8.   8     const TCB_t * const pxTCB = xTask;
  9.   9
  10. 10     configASSERT( pxTCB );
  11. 11
  12. 12     #if ( configNUMBER_OF_CORES == 1 )
  13. 13         if( pxTCB == pxCurrentTCB )
  14. 14         {
  15. 15             /* The task calling this function is querying its own state. */
  16. 16             /* 当前任务是查询的任务,则处于运行态 */
  17. 17             eReturn = eRunning;
  18. 18         }
  19. 19         else
  20. 20     #endif
  21. 21     {
  22. 22         taskENTER_CRITICAL();
  23. 23         {
  24. 24             pxStateList = listLIST_ITEM_CONTAINER( &( pxTCB->xStateListItem ) );
  25. 25             pxEventList = listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) );
  26. 26             pxDelayedList = pxDelayedTaskList;
  27. 27             pxOverflowedDelayedList = pxOverflowDelayedTaskList;
  28. 28         }
  29. 29         taskEXIT_CRITICAL();
  30. 30
  31. 31         if( pxEventList == &xPendingReadyList )
  32. 32         {
  33. 33             /* The task has been placed on the pending ready list, so its
  34. 34              * state is eReady regardless of what list the task's state list
  35. 35              * item is currently placed on. */
  36. 36             /* 任务的事件项处于xPendingReadyList中就是就绪态
  37. 37              * 说明xPendingReadyList里放的是任务的xEventListItem
  38. 38              * 调度器挂起时,任务已ready,调度器运行后,
  39. 39              * 会将xPendingReadyList中的任务移至就绪列表中 */
  40. 40             eReturn = eReady;
  41. 41         }
  42. 42         else if( ( pxStateList == pxDelayedList ) || ( pxStateList == pxOverflowedDelayedList ) )
  43. 43         {
  44. 44             /* The task being queried is referenced from one of the Blocked
  45. 45              * lists. */
  46. 46             /* pxDelayedTaskList和pxOverflowDelayedTaskList放的是阻塞任务,xStateListItem */
  47. 47             eReturn = eBlocked;
  48. 48         }
  49. 49
  50. 50         #if ( INCLUDE_vTaskSuspend == 1 )
  51. 51         /* xSuspendedTaskList放的是挂起的任务,xStateListItem */
  52. 52         else if( pxStateList == &xSuspendedTaskList )
  53. 53         {
  54. 54             /* The task being queried is referenced from the suspended
  55. 55              * list.  Is it genuinely suspended or is it blocked
  56. 56              * indefinitely? */
  57. 57             /* xEventListItem不在任何列表里 */
  58. 58             if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL )
  59. 59             {
  60. 60                 #if ( configUSE_TASK_NOTIFICATIONS == 1 )
  61. 61                 {
  62. 62                     BaseType_t x;
  63. 63
  64. 64                     /* The task does not appear on the event list item of
  65. 65                      * and of the RTOS objects, but could still be in the
  66. 66                      * blocked state if it is waiting on its notification
  67. 67                      * rather than waiting on an object.  If not, is
  68. 68                      * suspended. */
  69. 69                     /* 说明等通知不是把xEventListItem放到某个列表里,
  70. 70                      * 而且等通知会把任务放到挂起的列表里 */
  71. 71                     eReturn = eSuspended;
  72. 72
  73. 73                     for( x = ( BaseType_t ) 0; x < ( BaseType_t ) configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ )
  74. 74                     {
  75. 75                         if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION )
  76. 76                         {
  77. 77                             eReturn = eBlocked;
  78. 78                             break;
  79. 79                         }
  80. 80                     }
  81. 81                 }
  82. 82                 #else /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */
  83. 83                 {
  84. 84                     eReturn = eSuspended;
  85. 85                 }
  86. 86                 #endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */
  87. 87             }
  88. 88             else
  89. 89             {
  90. 90                 /* 如果xStateListItem在挂起列表里且xEventListItem处于某个列表里,则任务会阻塞 */
  91. 91                 eReturn = eBlocked;
  92. 92             }
  93. 93         }
  94. 94         #endif /* if ( INCLUDE_vTaskSuspend == 1 ) */
  95. 95
  96. 96         #if ( INCLUDE_vTaskDelete == 1 )
  97. 97             else if( ( pxStateList == &xTasksWaitingTermination ) || ( pxStateList == NULL ) )
  98. 98             {
  99. 99                 /* The task being queried is referenced from the deleted
  100. 100                  * tasks list, or it is not referenced from any lists at
  101. 101                  * all. */
  102. 102                 /* 等着被删除,这很明显 */
  103. 103                 eReturn = eDeleted;
  104. 104             }
  105. 105         #endif
  106. 106
  107. 107         else
  108. 108         {
  109. 109             #if ( configNUMBER_OF_CORES == 1 )
  110. 110             {
  111. 111                 /* If the task is not in any other state, it must be in the
  112. 112                  * Ready (including pending ready) state. */
  113. 113                 /* 剩下的就只能是在就绪列表里(pxReadyTasksLists)
  114. 114                  * pxReadyTasksLists是个列表数组,通过排除法,可以
  115. 115                  * 免于进行遍历操作。 */
  116. 116                 eReturn = eReady;
  117. 117             }
  118. 118             #else /* #if ( configNUMBER_OF_CORES == 1 ) */
  119. 119             {
  120. 120                 if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE )
  121. 121                 {
  122. 122                     /* Is it actively running on a core? */
  123. 123                     eReturn = eRunning;
  124. 124                 }
  125. 125                 else
  126. 126                 {
  127. 127                     /* If the task is not in any other state, it must be in the
  128. 128                      * Ready (including pending ready) state. */
  129. 129                     eReturn = eReady;
  130. 130                 }
  131. 131             }
  132. 132             #endif /* #if ( configNUMBER_OF_CORES == 1 ) */
  133. 133         }
  134. 134     }
  135. 135
  136. 136     /* 简单总结下:xStateListItem会在pxDelayedTaskList、pxOverflowDelayedTaskList、
  137. 137      * xSuspendedTaskList和xTasksWaitingTermination里
  138. 138      * xEventListItem会在xPendingReadyList里。
  139. 139      * 简单分析就是事件、通知来了任务就放到xPendingReadyList里,其他情况就在其他列表里
  140. 140      * 具体是不是这样,看后面的源码,会逐渐清晰 */
  141. 141
  142. 142     return eReturn;
  143. 143 }
复制代码
eTaskGetState4.2.8 任务延迟,释放CPU--vTaskDelay

这个接口比较熟悉,至少是任务中最常用的接口了。但是正如接口的注释所说的那样,这个接口不提供准确的延迟时间,任务唤醒的周期会受中断、其他任务的影响,并且计时起始于这个接口开始被调用的时候,且时间是大于等于给予的延迟时间。如果想要准确的周期性的任务,那就要调用xTaskDelayUntil接口,这个会在后面讲到。
 
7.gif
8.gif
  1.   1 void vTaskDelay( const TickType_t xTicksToDelay )
  2.   2 {
  3.   3     BaseType_t xAlreadyYielded = pdFALSE;
  4.   4
  5.   5     /* A delay time of zero just forces a reschedule. */
  6.   6     /* 如果延迟tick为0, 则表示强制执行调度 */
  7.   7     if( xTicksToDelay > ( TickType_t ) 0U )
  8.   8     {
  9.   9         vTaskSuspendAll();    // 停止调度器
  10. 10         {
  11. 11             configASSERT( uxSchedulerSuspended == 1U );
  12. 12
  13. 13             /* A task that is removed from the event list while the
  14. 14              * scheduler is suspended will not get placed in the ready
  15. 15              * list or removed from the blocked list until the scheduler
  16. 16              * is resumed.
  17. 17              *
  18. 18              * This task cannot be in an event list as it is the currently
  19. 19              * executing task. */
  20. 20             /* 在调度器暂停时, 从事件列表中删除的任务将不会被放置在就绪列表中
  21. 21              * 或从阻塞列表中删除, 直到调度器恢复。此任务不可能在事件列表中,
  22. 22              * 因为它是当前正在执行的任务。 */
  23. 23             prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );
  24. 24         }
  25. 25         xAlreadyYielded = xTaskResumeAll();
  26. 26     }
  27. 27     else
  28. 28     {
  29. 29         mtCOVERAGE_TEST_MARKER();
  30. 30     }
  31. 31
  32. 32     /* Force a reschedule if xTaskResumeAll has not already done so, we may
  33. 33      * have put ourselves to sleep. */
  34. 34     /* xTaskResumeAll没有调度, 那在这里手动调度 */
  35. 35     if( xAlreadyYielded == pdFALSE )
  36. 36     {
  37. 37         taskYIELD_WITHIN_API();
  38. 38     }
  39. 39     else
  40. 40     {
  41. 41         mtCOVERAGE_TEST_MARKER();
  42. 42     }
  43. 43 }
  44. 44
  45. 45 void vTaskSuspendAll( void )
  46. 46 {
  47. 47     #if ( configNUMBER_OF_CORES == 1 )
  48. 48     {
  49. 49         /* A critical section is not required as the variable is of type
  50. 50          * BaseType_t.  Please read Richard Barry's reply in the following link to a
  51. 51          * post in the FreeRTOS support forum before reporting this as a bug! -
  52. 52          * https://goo.gl/wu4acr */
  53. 53
  54. 54         /* portSOFTWARE_BARRIER() is only implemented for emulated/simulated ports that
  55. 55          * do not otherwise exhibit real time behaviour. */
  56. 56         portSOFTWARE_BARRIER();
  57. 57
  58. 58         /* The scheduler is suspended if uxSchedulerSuspended is non-zero.  An increment
  59. 59          * is used to allow calls to vTaskSuspendAll() to nest. */
  60. 60         /* uxSchedulerSuspended非0, 则调度器暂停, 加1是为了嵌套调用(中断也调用) */
  61. 61         uxSchedulerSuspended = ( UBaseType_t ) ( uxSchedulerSuspended + 1U );
  62. 62
  63. 63         /* Enforces ordering for ports and optimised compilers that may otherwise place
  64. 64          * the above increment elsewhere. */
  65. 65         portMEMORY_BARRIER();
  66. 66     }
  67. 67     #endif /* #if ( configNUMBER_OF_CORES == 1 ) */
  68. 68 }
  69. 69
  70. 70 static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait,
  71. 71                                             const BaseType_t xCanBlockIndefinitely )
  72. 72 {
  73. 73     TickType_t xTimeToWake;
  74. 74     const TickType_t xConstTickCount = xTickCount;
  75. 75     List_t * const pxDelayedList = pxDelayedTaskList;
  76. 76     List_t * const pxOverflowDelayedList = pxOverflowDelayedTaskList;
  77. 77
  78. 78     #if ( INCLUDE_xTaskAbortDelay == 1 )
  79. 79     {
  80. 80         /* About to enter a delayed list, so ensure the ucDelayAborted flag is
  81. 81          * reset to pdFALSE so it can be detected as having been set to pdTRUE
  82. 82          * when the task leaves the Blocked state. */
  83. 83         pxCurrentTCB->ucDelayAborted = ( uint8_t ) pdFALSE;
  84. 84     }
  85. 85     #endif
  86. 86
  87. 87     /* Remove the task from the ready list before adding it to the blocked list
  88. 88      * as the same list item is used for both lists. */
  89. 89     /* 将当前任务移出的列表必然是就绪列表, 移出后的就绪列表的item数量为0, 表示
  90. 90      * 就绪列表为空了, 需要重置下uxTopReadyPriority */
  91. 91     if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
  92. 92     {
  93. 93         /* The current task must be in a ready list, so there is no need to
  94. 94          * check, and the port reset macro can be called directly. */
  95. 95         portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );
  96. 96     }
  97. 97     else
  98. 98     {
  99. 99         mtCOVERAGE_TEST_MARKER();
  100. 100     }
  101. 101
  102. 102     #if ( INCLUDE_vTaskSuspend == 1 )
  103. 103     {
  104. 104         if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) )
  105. 105         {
  106. 106             /* Add the task to the suspended task list instead of a delayed task
  107. 107              * list to ensure it is not woken by a timing event.  It will block
  108. 108              * indefinitely. */
  109. 109             /* 无限等待, 就加入到挂起列表里 */
  110. 110             listINSERT_END( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) );
  111. 111         }
  112. 112         else
  113. 113         {
  114. 114             /* Calculate the time at which the task should be woken if the event
  115. 115              * does not occur.  This may overflow but this doesn't matter, the
  116. 116              * kernel will manage it correctly. */
  117. 117             /* 计算延迟结束的tick */
  118. 118             xTimeToWake = xConstTickCount + xTicksToWait;
  119. 119
  120. 120             /* The list item will be inserted in wake time order. */
  121. 121             /* 说明xStateListItem的项里的值, 放的是wake的时间点 */
  122. 122             listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );
  123. 123
  124. 124             if( xTimeToWake < xConstTickCount )
  125. 125             {
  126. 126                 /* Wake time has overflowed.  Place this item in the overflow
  127. 127                  * list. */
  128. 128                 /* 处理数值溢出的情况, 说明wake的tick值溢出的话, 任务就在pxOverflowDelayedTaskList
  129. 129                  * 列表里, 这是freeRTOS处理tick溢出的方式, 即用两个delay列表交替使用来处理 */
  130. 130                 traceMOVED_TASK_TO_OVERFLOW_DELAYED_LIST();
  131. 131                 vListInsert( pxOverflowDelayedList, &( pxCurrentTCB->xStateListItem ) );
  132. 132             }
  133. 133             else
  134. 134             {
  135. 135                 /* The wake time has not overflowed, so the current block list
  136. 136                  * is used. */
  137. 137                 /* 没溢出就放到pxDelayedTaskList列表里, 一般一旦有溢出, 后面的任务
  138. 138                  * 都是处于溢出的情况, 一旦tick加到0了, 就会交换pxDelayedTaskList
  139. 139                  * 和pxOverflowDelayedTaskList指向的列表, 这样就解决了溢出的问题 */
  140. 140                 traceMOVED_TASK_TO_DELAYED_LIST();
  141. 141                 vListInsert( pxDelayedList, &( pxCurrentTCB->xStateListItem ) );
  142. 142
  143. 143                 /* If the task entering the blocked state was placed at the
  144. 144                  * head of the list of blocked tasks then xNextTaskUnblockTime
  145. 145                  * needs to be updated too. */
  146. 146                 /* 更新最近唤醒任务的时间 */
  147. 147                 if( xTimeToWake < xNextTaskUnblockTime )
  148. 148                 {
  149. 149                     xNextTaskUnblockTime = xTimeToWake;
  150. 150                 }
  151. 151                 else
  152. 152                 {
  153. 153                     mtCOVERAGE_TEST_MARKER();
  154. 154                 }
  155. 155             }
  156. 156         }
  157. 157     }
  158. 158     #endif /* INCLUDE_vTaskSuspend */
  159. 159 }
复制代码
vTaskDelay  vTaskDelay的流程图如下。
9.jpg

 
  接着是vTaskDelay调用的几个接口,除了xTaskIncrementTick接口没有放进来,这个接口比较重要和复杂,后面专门会进行解析。
  4.2.8.1 将当前任务加入到延迟列表--prvAddCurrentTaskToDelayedList

10.gif
11.gif
  1. 1 static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait,
  2. 2                                             const BaseType_t xCanBlockIndefinitely )
  3. 3 {
  4. 4     TickType_t xTimeToWake;
  5. 5     const TickType_t xConstTickCount = xTickCount;
  6. 6     List_t * const pxDelayedList = pxDelayedTaskList;
  7. 7     List_t * const pxOverflowDelayedList = pxOverflowDelayedTaskList;
  8. 8
  9. 9     #if ( INCLUDE_xTaskAbortDelay == 1 )
  10. 10     {
  11. 11         /* About to enter a delayed list, so ensure the ucDelayAborted flag is
  12. 12          * reset to pdFALSE so it can be detected as having been set to pdTRUE
  13. 13          * when the task leaves the Blocked state. */
  14. 14         pxCurrentTCB->ucDelayAborted = ( uint8_t ) pdFALSE;
  15. 15     }
  16. 16     #endif
  17. 17
  18. 18     /* Remove the task from the ready list before adding it to the blocked list
  19. 19      * as the same list item is used for both lists. */
  20. 20     /* 将当前任务移出的列表必然是就绪列表, 移出后的就绪列表的item数量为0, 表示
  21. 21      * 就绪列表为空了, 需要重置下uxTopReadyPriority */
  22. 22     if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
  23. 23     {
  24. 24         /* The current task must be in a ready list, so there is no need to
  25. 25          * check, and the port reset macro can be called directly. */
  26. 26         portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );
  27. 27     }
  28. 28     else
  29. 29     {
  30. 30         mtCOVERAGE_TEST_MARKER();
  31. 31     }
  32. 32
  33. 33     #if ( INCLUDE_vTaskSuspend == 1 )
  34. 34     {
  35. 35         if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) )
  36. 36         {
  37. 37             /* Add the task to the suspended task list instead of a delayed task
  38. 38              * list to ensure it is not woken by a timing event.  It will block
  39. 39              * indefinitely. */
  40. 40             /* 无限等待, 就加入到挂起列表里 */
  41. 41             listINSERT_END( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) );
  42. 42         }
  43. 43         else
  44. 44         {
  45. 45             /* Calculate the time at which the task should be woken if the event
  46. 46              * does not occur.  This may overflow but this doesn't matter, the
  47. 47              * kernel will manage it correctly. */
  48. 48             /* 计算延迟结束的tick */
  49. 49             xTimeToWake = xConstTickCount + xTicksToWait;
  50. 50
  51. 51             /* The list item will be inserted in wake time order. */
  52. 52             /* 说明xStateListItem的项里的值, 放的是wake的时间点 */
  53. 53             listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );
  54. 54
  55. 55             if( xTimeToWake < xConstTickCount )
  56. 56             {
  57. 57                 /* Wake time has overflowed.  Place this item in the overflow
  58. 58                  * list. */
  59. 59                 /* 处理数值溢出的情况, 说明wake的tick值溢出的话, 任务就在pxOverflowDelayedTaskList
  60. 60                  * 列表里, 这是freeRTOS处理tick溢出的方式, 即用两个delay列表交替使用来处理 */
  61. 61                 traceMOVED_TASK_TO_OVERFLOW_DELAYED_LIST();
  62. 62                 vListInsert( pxOverflowDelayedList, &( pxCurrentTCB->xStateListItem ) );
  63. 63             }
  64. 64             else
  65. 65             {
  66. 66                 /* The wake time has not overflowed, so the current block list
  67. 67                  * is used. */
  68. 68                 /* 没溢出就放到pxDelayedTaskList列表里, 一般一旦有溢出, 后面的任务
  69. 69                  * 都是处于溢出的情况, 一旦tick加到0了, 就会交换pxDelayedTaskList
  70. 70                  * 和pxOverflowDelayedTaskList指向的列表, 这样就解决了溢出的问题 */
  71. 71                 traceMOVED_TASK_TO_DELAYED_LIST();
  72. 72                 vListInsert( pxDelayedList, &( pxCurrentTCB->xStateListItem ) );
  73. 73
  74. 74                 /* If the task entering the blocked state was placed at the
  75. 75                  * head of the list of blocked tasks then xNextTaskUnblockTime
  76. 76                  * needs to be updated too. */
  77. 77                 /* 更新最近唤醒任务的时间 */
  78. 78                 if( xTimeToWake < xNextTaskUnblockTime )
  79. 79                 {
  80. 80                     xNextTaskUnblockTime = xTimeToWake;
  81. 81                 }
  82. 82                 else
  83. 83                 {
  84. 84                     mtCOVERAGE_TEST_MARKER();
  85. 85                 }
  86. 86             }
  87. 87         }
  88. 88     }
  89. 89     #endif /* INCLUDE_vTaskSuspend */
  90. 90 }
复制代码
prvAddCurrentTaskToDelayedList  4.2.8.2 停止所有任务(停止调度器)--vTaskSuspendAll

12.gif
13.gif
  1. 1 void vTaskSuspendAll( void )
  2. 2 {
  3. 3     #if ( configNUMBER_OF_CORES == 1 )
  4. 4     {
  5. 5         /* A critical section is not required as the variable is of type
  6. 6          * BaseType_t.  Please read Richard Barry's reply in the following link to a
  7. 7          * post in the FreeRTOS support forum before reporting this as a bug! -
  8. 8          * https://goo.gl/wu4acr */
  9. 9
  10. 10         /* portSOFTWARE_BARRIER() is only implemented for emulated/simulated ports that
  11. 11          * do not otherwise exhibit real time behaviour. */
  12. 12         portSOFTWARE_BARRIER();
  13. 13
  14. 14         /* The scheduler is suspended if uxSchedulerSuspended is non-zero.  An increment
  15. 15          * is used to allow calls to vTaskSuspendAll() to nest. */
  16. 16         /* uxSchedulerSuspended非0, 则调度器暂停, 加1是为了嵌套调用(中断也调用) */
  17. 17         uxSchedulerSuspended = ( UBaseType_t ) ( uxSchedulerSuspended + 1U );
  18. 18
  19. 19         /* Enforces ordering for ports and optimised compilers that may otherwise place
  20. 20          * the above increment elsewhere. */
  21. 21         portMEMORY_BARRIER();
  22. 22     }
  23. 23     #endif /* #if ( configNUMBER_OF_CORES == 1 ) */
  24. 24 }
复制代码
vTaskSuspendAll  4.2.8.3 继续所有任务(继续调度器)--xTaskResumeAll

  这个接口返回的是否已经调度过。
14.gif
15.gif
  1.   1 BaseType_t xTaskResumeAll( void )
  2.   2 {
  3.   3     TCB_t * pxTCB = NULL;
  4.   4     BaseType_t xAlreadyYielded = pdFALSE;
  5.   5
  6.   6     {
  7.   7         /* It is possible that an ISR caused a task to be removed from an event
  8.   8          * list while the scheduler was suspended.  If this was the case then the
  9.   9          * removed task will have been added to the xPendingReadyList.  Once the
  10. 10          * scheduler has been resumed it is safe to move all the pending ready
  11. 11          * tasks from this list into their appropriate ready list. */
  12. 12         taskENTER_CRITICAL();
  13. 13         {
  14. 14             BaseType_t xCoreID;
  15. 15             xCoreID = ( BaseType_t ) portGET_CORE_ID();    // 单核默认返回0
  16. 16
  17. 17             /* If uxSchedulerSuspended is zero then this function does not match a
  18. 18              * previous call to vTaskSuspendAll(). */
  19. 19             /* vTaskSuspendAll和xTaskResumeAll成对调用, 所以前面必然是调用过了
  20. 20              * vTaskSuspendAll, 那么uxSchedulerSuspended必然大于0的 */
  21. 21             configASSERT( uxSchedulerSuspended != 0U );
  22. 22
  23. 23             uxSchedulerSuspended = ( UBaseType_t ) ( uxSchedulerSuspended - 1U );
  24. 24             portRELEASE_TASK_LOCK();
  25. 25
  26. 26             if( uxSchedulerSuspended == ( UBaseType_t ) 0U )
  27. 27             {
  28. 28                 if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U )
  29. 29                 {
  30. 30                     /* Move any readied tasks from the pending list into the
  31. 31                      * appropriate ready list. */
  32. 32                     /* 为什么是xPendingReadyList, 因为调用vTaskSuspendAll后调度器暂停了,
  33. 33                      * 如果在调用本接口前(本接口会进入临界区, 临界区会禁止所有系统中断)
  34. 34                      * 有任务就绪了(中断、事件、通知等), 就会把任务放xPendingReadyList列表里 */
  35. 35                     while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE )
  36. 36                     {
  37. 37                         /* 把xPendingReadyList里的所有任务一个一个放到就绪列表中 */
  38. 38                         pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) );
  39. 39                         listREMOVE_ITEM( &( pxTCB->xEventListItem ) );
  40. 40                         portMEMORY_BARRIER();
  41. 41                         listREMOVE_ITEM( &( pxTCB->xStateListItem ) );
  42. 42                         prvAddTaskToReadyList( pxTCB );
  43. 43
  44. 44                         #if ( configNUMBER_OF_CORES == 1 )
  45. 45                         {
  46. 46                             /* If the moved task has a priority higher than the current
  47. 47                              * task then a yield must be performed. */
  48. 48                             /* 如果取出来的任务有优先级比当前任务高的, 就需要调度, 先置标志位 */
  49. 49                             if( pxTCB->uxPriority > pxCurrentTCB->uxPriority )
  50. 50                             {
  51. 51                                 xYieldPendings[ xCoreID ] = pdTRUE;
  52. 52                             }
  53. 53                             else
  54. 54                             {
  55. 55                                 mtCOVERAGE_TEST_MARKER();
  56. 56                             }
  57. 57                         }
  58. 58                         #endif /* #if ( configNUMBER_OF_CORES == 1 ) */
  59. 59                     }
  60. 60
  61. 61                     if( pxTCB != NULL )
  62. 62                     {
  63. 63                         /* A task was unblocked while the scheduler was suspended,
  64. 64                          * which may have prevented the next unblock time from being
  65. 65                          * re-calculated, in which case re-calculate it now.  Mainly
  66. 66                          * important for low power tickless implementations, where
  67. 67                          * this can prevent an unnecessary exit from low power
  68. 68                          * state. */
  69. 69                         /* 任务在调度器暂停时解除阻塞, 立即计算可以防止下一个解除阻塞
  70. 70                          * 时间被重新计算, 这对于低功耗无障碍实现非常重要,因为这可以
  71. 71                          * 防止不必要地退出低功耗 */
  72. 72                         prvResetNextTaskUnblockTime();
  73. 73                     }
  74. 74
  75. 75                     /* If any ticks occurred while the scheduler was suspended then
  76. 76                      * they should be processed now.  This ensures the tick count does
  77. 77                      * not  slip, and that any delayed tasks are resumed at the correct
  78. 78                      * time.
  79. 79                      *
  80. 80                      * It should be safe to call xTaskIncrementTick here from any core
  81. 81                      * since we are in a critical section and xTaskIncrementTick itself
  82. 82                      * protects itself within a critical section. Suspending the scheduler
  83. 83                      * from any core causes xTaskIncrementTick to increment uxPendedCounts. */
  84. 84                     /* 在调度器停止的时候, tick增加是增加xPendedTicks这个值, 在这里恢复调度器
  85. 85                      * 的时候需要把这些增加的xPendedTicks值处理掉, 在临界区调用xTaskIncrementTick
  86. 86                      * 是安全的, 至于为什么在解析xTaskIncrementTick后应该会清楚 */
  87. 87                     {
  88. 88                         TickType_t xPendedCounts = xPendedTicks; /* Non-volatile copy. */
  89. 89
  90. 90                         if( xPendedCounts > ( TickType_t ) 0U )
  91. 91                         {
  92. 92                             do
  93. 93                             {
  94. 94                                 if( xTaskIncrementTick() != pdFALSE )
  95. 95                                 {
  96. 96                                     /* Other cores are interrupted from
  97. 97                                      * within xTaskIncrementTick(). */
  98. 98                                     xYieldPendings[ xCoreID ] = pdTRUE;
  99. 99                                 }
  100. 100                                 else
  101. 101                                 {
  102. 102                                     mtCOVERAGE_TEST_MARKER();
  103. 103                                 }
  104. 104
  105. 105                                 --xPendedCounts;
  106. 106                             } while( xPendedCounts > ( TickType_t ) 0U );
  107. 107
  108. 108                             xPendedTicks = 0;
  109. 109                         }
  110. 110                         else
  111. 111                         {
  112. 112                             mtCOVERAGE_TEST_MARKER();
  113. 113                         }
  114. 114                     }
  115. 115
  116. 116                     /* 需要调度的话, 这里立即开始调度(实际应该是
  117. 117                      * 等退出临界区后进入pendSV中断), 我暂时也不确定,
  118. 118                      * 等有空了仿真调式看看 */
  119. 119                     if( xYieldPendings[ xCoreID ] != pdFALSE )
  120. 120                     {
  121. 121                         #if ( configUSE_PREEMPTION != 0 )
  122. 122                         {
  123. 123                             xAlreadyYielded = pdTRUE;
  124. 124                         }
  125. 125                         #endif /* #if ( configUSE_PREEMPTION != 0 ) */
  126. 126
  127. 127                         #if ( configNUMBER_OF_CORES == 1 )
  128. 128                         {
  129. 129                             taskYIELD_TASK_CORE_IF_USING_PREEMPTION( pxCurrentTCB );
  130. 130                         }
  131. 131                         #endif /* #if ( configNUMBER_OF_CORES == 1 ) */
  132. 132                     }
  133. 133                     else
  134. 134                     {
  135. 135                         mtCOVERAGE_TEST_MARKER();
  136. 136                     }
  137. 137                 }
  138. 138             }
  139. 139             else
  140. 140             {
  141. 141                 mtCOVERAGE_TEST_MARKER();
  142. 142             }
  143. 143         }
  144. 144         taskEXIT_CRITICAL();
  145. 145     }
  146. 146
  147. 147     return xAlreadyYielded;
  148. 148 }
复制代码
xTaskResumeAll 
  这篇就解析到这里,里面有不少遗留的问题,相信在逐步解析的过程中会慢慢解决的。下一篇开始解析xTaskDelayUntil和vTaskPrioritySet等接口,一些简单的接口如uxTaskBasePriorityGet这些,应该会选一个举例,其他的可能就一笔带过了。
  那么,下一篇再见。
 

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册