前置知识

  • C语言基础及其指针
  • 数据结构与算法基础
  • 汇编基础
  • FreeRTOS以下简称OS
  • trace前缀开头的是用来调试追溯的

OS结构体解析

  • 每个任务创建后都会有一个TCB结构的数据
    • TCB(Task Control Block),用于存放任务的参数,包含栈地址、栈顶指针、优先级、状态链表和事件链表
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      typedef struct tskTaskControlBlock      
      {
          volatile StackType_t * pxTopOfStack; // 此参数适中指向任务的(TCB)栈顶,并且必须放到当前结构体(TCB)的第一个位置,因为后面汇编会通过偏移直接访问
         
          #if ( portUSING_MPU_WRAPPERS == 1 )
              xMPU_SETTINGS xMPUSettings; // 存放MPU的相关设置
          #endif
         
          #if ( configUSE_CORE_AFFINITY == 1 ) && ( configNUMBER_OF_CORES > 1 )
              UBaseType_t uxCoreAffinityMask; // 在多核系统中,用于指定任务可以运行在哪个核心上,通过掩码的方式
          #endif

          ListItem_t xStateListItem; // 任务的状态链表结点,用于将任务挂载到任务列表中,根据不同的优先级挂载到不同的索引下

          ListItem_t xEventListItem; // 事件链表结点

          UBaseType_t uxPriority; // 任务的优先级,0为最低优先级
          StackType_t * pxStack; // 任务栈的地址,始终指向栈底(栈的开始)

          #if ( configNUMBER_OF_CORES > 1 )
              volatile BaseType_t xTaskRunState;      /**< Used to identify the core the task is running on, if the task is running. Otherwise, identifies the task's state - not running or yielding. */
              UBaseType_t uxTaskAttributes;           /**< Task's attributes - currently used to identify the idle tasks. */
          #endif

          char pcTaskName[ configMAX_TASK_NAME_LEN ]; // 存放任务的名称,最大长度为16

          #if ( configUSE_TASK_PREEMPTION_DISABLE == 1 )
              BaseType_t xPreemptionDisable; // 当OS配置为抢占时,禁止任务被被抢占。
          #endif

          #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
              StackType_t * pxEndOfStack; // 指向任务最高合法的地址
          #endif

          #if ( portCRITICAL_NESTING_IN_TCB == 1 )
              UBaseType_t uxCriticalNesting; // 用于记录临界区的嵌套深度,每当进入一个临界区此值递增若退出递减
          #endif

          #if ( configUSE_TRACE_FACILITY == 1 )
              UBaseType_t uxTCBNumber;  // 存放了一个递增的数值,每次任务创建此值自动递增,用于调试器识别任务是否被删除或者重建
              UBaseType_t uxTaskNumber; // 存放了一个数字,用于第三方工具调试追踪
          #endif

          #if ( configUSE_MUTEXES == 1 )
              UBaseType_t uxBasePriority; // 上一次为该任务分配的优先级,用于互斥锁继承机制
              UBaseType_t uxMutexesHeld; // 任务当前持有的互斥锁数量
          #endif

          #if ( configUSE_APPLICATION_TASK_TAG == 1 )
              TaskHookFunction_t pxTaskTag;
          #endif

          #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 )
              void * pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ];
          #endif

          #if ( configGENERATE_RUN_TIME_STATS == 1 )
              configRUN_TIME_COUNTER_TYPE ulRunTimeCounter; /**< Stores the amount of time the task has spent in the Running state. */
          #endif

          #if ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 )
              configTLS_BLOCK_TYPE xTLSBlock; /**< Memory block used as Thread Local Storage (TLS) Block for the task. */
          #endif

          #if ( configUSE_TASK_NOTIFICATIONS == 1 )
          // 每个OS任务都有一个任务通知数组。每个任务都有“挂起”或“非挂起”的通知状态和一个32位的通知值
              volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; // 任务通知值
              volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; // 任务通知状态
          #endif

          /* See the comments in FreeRTOS.h with the definition of
           * tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */
          #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
              uint8_t ucStaticallyAllocated; // 如果任务是通过静态方法创建的则为pdTRUE,确保不会尝试释放当前任务

          #if ( INCLUDE_xTaskAbortDelay == 1 )
              uint8_t ucDelayAborted; // 标记任务的延迟状态是否被中断(如通过 `xTaskAbortDelay()` 强制唤醒任务)
          #endif

          #if ( configUSE_POSIX_ERRNO == 1 )
              int iTaskErrno;
          #endif

      } tskTCB;

      OS的任务创建分析

  • 在OS中任务创建中有两个函数可以创建OS任务,以下是两个函数的简单介绍
    • xTaskCreate:动态分配一个大小为sizeof(TCB)的内存块,包括栈等,存放TCB数据
    • xTaskCreateStatic:通过用户自己定义TCB内存块,包括栈,并且必须是持续存在的不能在函数栈里声明

xTaskCreate动态创建任务

  • 该函数用于动态创建一个OS任务,以下是主要的参数说明
    • pxTaskCode:存放任务函数的地址
    • pcName:任务的名称
    • uxStackDepth:栈深度,即栈的大小
    • pvParameters:任务参数
    • uxPriority:任务的优先级
    • pxCreatedTask:用于接收任务句柄
  • 以下是该函数的解析
  • 声明了一个TCB指针用于存放待会创建后的TCB数据
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
        BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,

                                const char * const pcName,

                                const configSTACK_DEPTH_TYPE uxStackDepth,

                                void * const pvParameters,

                                UBaseType_t uxPriority,

                                TaskHandle_t * const pxCreatedTask )

        {

            TCB_t * pxNewTCB;

            BaseType_t xReturn;



            traceENTER_xTaskCreate( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask );



            pxNewTCB = prvCreateTask( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask ); // 调用`prvCreateTask`创建任务,并将参数传递



            if( pxNewTCB != NULL ) // 判断任务是否创建成功

            {

                #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) )

                {

                    /* Set the task's affinity before scheduling it. */
    // 设置任务的所使用的核心,默认则是使用所有核心
                    pxNewTCB->uxCoreAffinityMask = configTASK_DEFAULT_CORE_AFFINITY;

                }

                #endif



                prvAddNewTaskToReadyList( pxNewTCB ); // 调用`prvAddNewTaskToReadyList`将创建的任务添加到就绪列表,待调度器进行调度

                xReturn = pdPASS;

            }

            else // 创建失败

            {

                xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; // 返回内存不足

            }



            traceRETURN_xTaskCreate( xReturn );



            return xReturn; // 返回任务创建状态码,若TCB创建成功则返回1表示成功,否则为-1表示内存不足以存放TCB数据

        }

prvCreateTask创建任务核心

  • 若开启了动态创建任务则使用函数,否则编译器将其代码优化以用于裁剪压缩
  • 根据栈的增长方向的不同,进行任务栈和TCB内存块的分配。
  • 若分配失败,则回收内存防止OOM(Out Of Memory)
  • 若分配成功,调用任务初始化函数对任务进行初始化操作
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
      
    #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )

        static TCB_t * prvCreateTask( TaskFunction_t pxTaskCode,

                                      const char * const pcName,

                                      const configSTACK_DEPTH_TYPE uxStackDepth,

                                      void * const pvParameters,

                                      UBaseType_t uxPriority,

                                      TaskHandle_t * const pxCreatedTask )

        {

            TCB_t * pxNewTCB;



            /* If the stack grows down then allocate the stack then the TCB so the stack

             * does not grow into the TCB.  Likewise if the stack grows up then allocate

             * the TCB then the stack.
           
            首先判断栈的增长方向,若大于0则向上增长,小于0为向下增长
             
              若栈为向上增长,先分配TCB内存块再分配任务栈内存块,栈帧模型如下
    低地址 → +-----------------+
    | TCB | ← 先分配,在低地址侧
    +-----------------+
    | 栈 | ← 后分配,栈从这里开始,向高地址生长
    高地址 → +-----------------+
    此时若栈溢出,栈会往高地址增加,不会影响核心数据TCB

    若栈为向下增长时,先分配栈内存块再分配TCB内存块,栈帧模型如下
    高地址 → +-----------------+
    | 栈 | ← 栈从这里开始,向低地址生长
    +-----------------+
    | TCB | ← 后分配,在栈的低地址侧
    低地址 → +-----------------+
    TCB在栈的下方,即TCB地址比栈低。若此时发生了栈溢出会影响到TCB的数据,但OS内部有栈溢出检测


    */

            #if ( portSTACK_GROWTH > 0 )

            {

                /* Allocate space for the TCB.  Where the memory comes from depends on

                 * the implementation of the port malloc function and whether or not static

                 * allocation is being used. */

                /* MISRA Ref 11.5.1 [Malloc memory assignment] */

                /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */

                /* coverity[misra_c_2012_rule_11_5_violation] */
    // 分配内存块用于存放TCB
                pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );



                if( pxNewTCB != NULL ) // 若为NULL则内存分配失败,反之

                {

                    ( void ) memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) );



                    /* Allocate space for the stack used by the task being created.

                     * The base of the stack memory stored in the TCB so the task can

                     * be deleted later if required. */

                    /* MISRA Ref 11.5.1 [Malloc memory assignment] */

                    /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */

                    /* coverity[misra_c_2012_rule_11_5_violation] */

                    pxNewTCB->pxStack = ( StackType_t * ) pvPortMallocStack( ( ( ( size_t ) uxStackDepth ) * sizeof( StackType_t ) ) );



                    if( pxNewTCB->pxStack == NULL ) // 同上

                    {

                        /* Could not allocate the stack.  Delete the allocated TCB. */

                        vPortFree( pxNewTCB ); // pxNewTCB->pxStack为NULL则说明内存分配失败,回收内存防止OOM

                        pxNewTCB = NULL;

                    }

                }

            }

            #else /* portSTACK_GROWTH */

            {

                StackType_t * pxStack;



                /* Allocate space for the stack used by the task being created. */

                /* MISRA Ref 11.5.1 [Malloc memory assignment] */

                /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */

                /* coverity[misra_c_2012_rule_11_5_violation] */

                pxStack = pvPortMallocStack( ( ( ( size_t ) uxStackDepth ) * sizeof( StackType_t ) ) );



                if( pxStack != NULL )

                {

                    /* Allocate space for the TCB. */

                    /* MISRA Ref 11.5.1 [Malloc memory assignment] */

                    /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */

                    /* coverity[misra_c_2012_rule_11_5_violation] */

                    pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );



                    if( pxNewTCB != NULL )

                    {

                        ( void ) memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) );



                        /* Store the stack location in the TCB. */

                        pxNewTCB->pxStack = pxStack;

                    }

                    else

                    {

                        /* The stack cannot be used as the TCB was not created.  Free

                         * it again. */

                        vPortFreeStack( pxStack ); // pxNewTCB为NULL则说明内存分配失败,释放掉TCB和任务栈内存块

                    }

                }

                else

                {

                    pxNewTCB = NULL;

                }

            }

            #endif /* portSTACK_GROWTH */



            if( pxNewTCB != NULL )

            {

                #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )

                {

                    /* Tasks can be created statically or dynamically, so note this

                     * task was created dynamically in case it is later deleted. */

                    pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB; // 设置任务静态标志为0表示动态分配的TCB和栈内存块

                }

                #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */



                prvInitialiseNewTask( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); // 调用`prvInitialiseNewTask`初始化任务

            }



            return pxNewTCB;

        }

prvInitialiseNewTask任务初始化

  • 首先判断portUSING_MPU_WRAPPERS(内存保护单元)是否为1,即开启了内存保护单元
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,

                                      const char * const pcName,

                                      const configSTACK_DEPTH_TYPE uxStackDepth,

                                      void * const pvParameters,

                                      UBaseType_t uxPriority,

                                      TaskHandle_t * const pxCreatedTask,

                                      TCB_t * pxNewTCB,

                                      const MemoryRegion_t * const xRegions )

    {

        StackType_t * pxTopOfStack;

        UBaseType_t x;



        #if ( portUSING_MPU_WRAPPERS == 1 )

            /* Should the task be created in privileged mode? */

            BaseType_t xRunPrivileged;


    // 若开启了MPU,判断其任务是否需要运行在特权模式,当运行在特权模式xRunPrivileged为pdTRUE,否则为pdFALSE
            if( ( uxPriority & portPRIVILEGE_BIT ) != 0U )

            {

                xRunPrivileged = pdTRUE;

            }

            else

            {

                xRunPrivileged = pdFALSE;

            }

            uxPriority &= ~portPRIVILEGE_BIT; // 清除最高位特权模式标志位,用于获取任务实际的优先级

        #endif /* portUSING_MPU_WRAPPERS == 1 */

    // 判断是否提供了memset依赖
        #if ( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1 )

        {

            /* Fill the stack with a known value to assist debugging. */

            ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) uxStackDepth * sizeof( StackType_t ) ); // 初始化内存块数据

        }

        #endif /* tskSET_NEW_STACKS_TO_KNOWN_VALUE */



        /* Calculate the top of stack address.  This depends on whether the stack

         * grows from high memory to low (as per the 80x86) or vice versa.

         * portSTACK_GROWTH is used to make the result positive or negative as required

         * by the port.
          计算栈顶地址,需要根据栈的增长方向进行不同的操作
          */

        #if ( portSTACK_GROWTH < 0 ) // 向下增长

        {

            pxTopOfStack = &( pxNewTCB->pxStack[ uxStackDepth - ( configSTACK_DEPTH_TYPE ) 1 ] ); // 设置栈的最后一个元素为栈顶

            pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); // 对栈地址进行字节对齐,字节对齐大小根据不同的平台架构设定,具体根据硬件的数据总线的数据宽度设定。假设使用的是Cortex-M3架构则需要进行4字节对齐,没对齐前的栈地址是0x20000006,对齐后0x20000004



            /* Check the alignment of the calculated top of stack is correct. */

            configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0U ) ); // 对对齐后的栈地址进行检测断言


    // 是否启用了记录栈底地址,若启用了则设置pxEndOfStack
            #if ( configRECORD_STACK_HIGH_ADDRESS == 1 )

            {

                /* Also record the stack's high address, which may assist

                 * debugging. */

                pxNewTCB->pxEndOfStack = pxTopOfStack;

            }

            #endif /* configRECORD_STACK_HIGH_ADDRESS */

        }

        #else /* portSTACK_GROWTH */ // 向上增长

        {

            pxTopOfStack = pxNewTCB->pxStack; // 栈顶即为栈的首地址

            pxTopOfStack = ( StackType_t * ) ( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) + portBYTE_ALIGNMENT_MASK ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); // 栈地址字节对齐



            /* Check the alignment of the calculated top of stack is correct. */

            configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0U ) );


            /* The other extreme of the stack space is required if stack checking is

             * performed. */

            pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( uxStackDepth - ( configSTACK_DEPTH_TYPE ) 1 ); // 记录栈底

        }

        #endif /* portSTACK_GROWTH */



        /* Store the task name in the TCB. */
        if( pcName != NULL ) // 任务是否有名称

        {

            for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) // 将名称从临时内存复制到TCB内存块里

            {

                pxNewTCB->pcTaskName[ x ] = pcName[ x ];



                /* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than

                 * configMAX_TASK_NAME_LEN characters just in case the memory after the

                 * string is not accessible (extremely unlikely). */

                if( pcName[ x ] == ( char ) 0x00 )

                {

                    break;

                }

                else

                {

                    mtCOVERAGE_TEST_MARKER();

                }

            }



            /* Ensure the name string is terminated in the case that the string length

             * was greater or equal to configMAX_TASK_NAME_LEN. */

            pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1U ] = '\0';

        }

        else

        {

            mtCOVERAGE_TEST_MARKER();

        }



        /* This is used as an array index so must ensure it's not too large. */

        configASSERT( uxPriority < configMAX_PRIORITIES ); // 优先级断言,若优先级超过最大优先级则断言



        if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) // 若超过了最高优先级

        {

            uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; // 设置优先级为最高优先级

        }

        else

        {

            mtCOVERAGE_TEST_MARKER();

        }



        pxNewTCB->uxPriority = uxPriority; // 将优先级记录到结构体里

        #if ( configUSE_MUTEXES == 1 )

        {

            pxNewTCB->uxBasePriority = uxPriority; // 是否使用了互斥锁,保留原有的优先级用于互斥锁继承机制

        }

        #endif /* configUSE_MUTEXES */



        vListInitialiseItem( &( pxNewTCB->xStateListItem ) ); // 初始化任务状态链表结点

        vListInitialiseItem( &( pxNewTCB->xEventListItem ) ); // 初始化事件状态链表结点



        /* Set the pxNewTCB as a link back from the ListItem_t.  This is so we can get

         * back to  the containing TCB from a generic item in a list. */

        listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB ); // 设置链表结点的拥有者为pxNewTCB



        /* Event lists are always in priority order. */

        listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); // 事件节点值为最高优先级减除当前的优先级,对其反向映射,越高的优先级此值越小

        listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB ); // 设置链表结点的拥有者为pxNewTCB



        #if ( portUSING_MPU_WRAPPERS == 1 )

        {

            vPortStoreTaskMPUSettings( &( pxNewTCB->xMPUSettings ), xRegions, pxNewTCB->pxStack, uxStackDepth ); // 若启用了MPU,设置允许访问的内存区域

        }

        #else

        {

            /* Avoid compiler warning about unreferenced parameter. */

            ( void ) xRegions;

        }

        #endif



        #if ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 ) // 是否开启了线程本地存储(Thread Local Storge)

        {

            /* Allocate and initialize memory for the task's TLS Block. */

            configINIT_TLS_BLOCK( pxNewTCB->xTLSBlock, pxTopOfStack ); // 设置TLS块

        }

        #endif



        /* Initialize the TCB stack to look as if the task was already running,

         * but had been interrupted by the scheduler.  The return address is set

         * to the start of the task function. Once the stack has been initialised

         * the top of stack variable is updated. */

        #if ( portUSING_MPU_WRAPPERS == 1 ) // 是否启用了MPU

        {

            /* If the port has capability to detect stack overflow,

             * pass the stack end address to the stack initialization

             * function as well. */

            #if ( portHAS_STACK_OVERFLOW_CHECKING == 1 ) // 是否开启了堆栈溢出检查

            {

                #if ( portSTACK_GROWTH < 0 ) // 堆栈的增长方向是递减的

                {

                    pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters, xRunPrivileged, &( pxNewTCB->xMPUSettings ) );

                }

                #else /* portSTACK_GROWTH */ // 递增的

                {

                    pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters, xRunPrivileged, &( pxNewTCB->xMPUSettings ) );

                }

                #endif /* portSTACK_GROWTH */

            }

            #else /* portHAS_STACK_OVERFLOW_CHECKING */

            {

                pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged, &( pxNewTCB->xMPUSettings ) );

            }

            #endif /* portHAS_STACK_OVERFLOW_CHECKING */

        }

        #else /* portUSING_MPU_WRAPPERS */

        {

            /* If the port has capability to detect stack overflow,

             * pass the stack end address to the stack initialization

             * function as well. */

            #if ( portHAS_STACK_OVERFLOW_CHECKING == 1 )

            {

                #if ( portSTACK_GROWTH < 0 )

                {

                    pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters );

                }

                #else /* portSTACK_GROWTH */

                {

                    pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters );

                }

                #endif /* portSTACK_GROWTH */

            }

            #else /* portHAS_STACK_OVERFLOW_CHECKING */

            {

                pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); // 初始化任务的堆栈

            }

            #endif /* portHAS_STACK_OVERFLOW_CHECKING */

        }

        #endif /* portUSING_MPU_WRAPPERS */



        /* Initialize task state and task attributes. */

        #if ( configNUMBER_OF_CORES > 1 ) // 是否是多核心

        {

            pxNewTCB->xTaskRunState = taskTASK_NOT_RUNNING; // 让任务没运行在任何核心上



            /* Is this an idle task? */

            if( ( ( TaskFunction_t ) pxTaskCode == ( TaskFunction_t ) prvIdleTask ) || ( ( TaskFunction_t ) pxTaskCode == ( TaskFunction_t ) prvPassiveIdleTask ) ) // 是否是空闲任务, 如果是设置任务空闲属性标志位

            {

                pxNewTCB->uxTaskAttributes |= taskATTRIBUTE_IS_IDLE;

            }

        }

        #endif /* #if ( configNUMBER_OF_CORES > 1 ) */



        if( pxCreatedTask != NULL )

        {

            /* Pass the handle out in an anonymous way.  The handle can be used to

             * change the created task's priority, delete the created task, etc.*/

            *pxCreatedTask = ( TaskHandle_t ) pxNewTCB; // 任务句柄

        }

        else

        {

            mtCOVERAGE_TEST_MARKER();

        }

    }

pxPortInitialiseStack初始化栈

  • 此函数是任务的核心,用于初始化堆栈模拟任务已经被执行了但被调度器中断了
  • 以下是GCC ARM_CM4F的函数分析,不同的架构有不同的函数,但其内容大同小异
  • 压入了部分数据,并分配了部分栈内存用于存放任务的寄存器数据,供调度器下次调度的时候恢复寄存器确保任务的正常调度
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    /*

     * See header file for description.

     */

    StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,

                                         TaskFunction_t pxCode,

                                         void * pvParameters )

    {

        /* Simulate the stack frame as it would be created by a context switch

         * interrupt. */



        /* Offset added to account for the way the MCU uses the stack on entry/exit

         * of interrupts, and to ensure alignment. */

        pxTopOfStack--; // 模拟栈sp--



        *pxTopOfStack = portINITIAL_XPSR; // 将数据压入栈                                    /* xPSR */

        pxTopOfStack--;

        *pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; // 程序计数器Program Counter /* PC */

        pxTopOfStack--;

        *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; // Link Register         /* LR */



        /* Save code space by skipping register initialisation. */

        pxTopOfStack -= 5;                            /* R12, R3, R2 and R1. */

        *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */



        /* A save method is being used that requires each task to maintain its

         * own exec return value. */

        pxTopOfStack--;

        *pxTopOfStack = portINITIAL_EXC_RETURN;



        pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */



        return pxTopOfStack;

    }
  • image.pngimage.png

prvAddNewTaskToReadyList添加任务到就绪列表

  • 此函数分为单核和多核两个,这里以单核为例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
     static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )

        {

            /* Ensure interrupts don't access the task lists while the lists are being

             * updated. */

            taskENTER_CRITICAL();

            {

                uxCurrentNumberOfTasks = ( UBaseType_t ) ( uxCurrentNumberOfTasks + 1U );



                if( pxCurrentTCB == NULL ) // 当前TCB是否为空,为空则表示当前没有任何任务执行,目前的任务是第一个任务

                {

                    /* There are no other tasks, or all the other tasks are in

                     * the suspended state - make this the current task. */

                    pxCurrentTCB = pxNewTCB; // 设置新建的任务的TCB为当前TCB



                    if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )

                    {

                        /* This is the first task to be created so do the preliminary

                         * initialisation required.  We will not recover if this call

                         * fails, but we will report the failure. */

                        prvInitialiseTaskLists(); // 若没有任务在列表里,则初始化任务列表

                    }

                    else

                    {

                        mtCOVERAGE_TEST_MARKER();

                    }

                }

                else

                {

                    /* If the scheduler is not already running, make this task the

                     * current task if it is the highest priority task to be created

                     * so far. */

                    if( xSchedulerRunning == pdFALSE )

                    {

                        if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority ) // 当前TCB存在表示当前有任务正在运行,若当前任务的优先级小于新任务则直接抢占将新任务设置成当前的任务

                        {

                            pxCurrentTCB = pxNewTCB;

                        }

                        else

                        {

                            mtCOVERAGE_TEST_MARKER();

                        }

                    }

                    else

                    {

                        mtCOVERAGE_TEST_MARKER();

                    }

                }



                uxTaskNumber++; // 任务数递增



                #if ( configUSE_TRACE_FACILITY == 1 )

                {

                    /* Add a counter into the TCB for tracing only. */

                    pxNewTCB->uxTCBNumber = uxTaskNumber;

                }

                #endif /* configUSE_TRACE_FACILITY */

                traceTASK_CREATE( pxNewTCB );



                prvAddTaskToReadyList( pxNewTCB ); // 插入任务到对应优先级的链表中



                portSETUP_TCB( pxNewTCB );

            }

            taskEXIT_CRITICAL();



            if( xSchedulerRunning != pdFALSE )

            {

                /* If the created task is of a higher priority than the current task

                 * then it should run now. */

                taskYIELD_ANY_CORE_IF_USING_PREEMPTION( pxNewTCB ); // 如果当前任务优先级小于新任务优先级则立刻触发PendSV中断进行任务调度,让其立刻执行新任务

            }

            else

            {

                mtCOVERAGE_TEST_MARKER();

            }

        }

    prvAddTaskToReadyList插入任务到链表中

  • 更新当前的最高优先级
  • 插入TCB到对应优先级索引的链表中
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #define prvAddTaskToReadyList( pxTCB )                                                                     \

        do {                                                                                                   \

            traceMOVED_TASK_TO_READY_STATE( pxTCB );                                                           \

            taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority );                                                \

            listINSERT_END( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \

            tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB );                                                      \

        } while( 0 )
  • image.png

任务调度分析

xPortSysTickHandlerOS心跳分析

  • 此函数在移植OS的时候需要周期性调用以给予OS时钟用于分片进行任务调度
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    void xPortSysTickHandler( void )

    {

        /* The SysTick runs at the lowest interrupt priority, so when this interrupt

         * executes all interrupts must be unmasked.  There is therefore no need to

         * save and then restore the interrupt mask value as its value is already

         * known. */

        portDISABLE_INTERRUPTS(); // 禁用所有中断,以防止操作被中断打断

        traceISR_ENTER();

        {

            /* Increment the RTOS tick. */

            if( xTaskIncrementTick() != pdFALSE ) // 调用函数`xTaskIncrementTick`对时钟Tick进行递增

            {

                traceISR_EXIT_TO_SCHEDULER();



                /* A context switch is required.  Context switching is performed in

                 * the PendSV interrupt.  Pend the PendSV interrupt. */

                portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;

            }

            else

            {

                traceISR_EXIT();

            }

        }

        portENABLE_INTERRUPTS(); // 恢复中断接收来自外部或内部的中断

    }

xTaskIncrementTick时钟核心

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
BaseType_t xTaskIncrementTick( void )

{

    TCB_t * pxTCB;

    TickType_t xItemValue;

    BaseType_t xSwitchRequired = pdFALSE;



    traceENTER_xTaskIncrementTick();



    /* Called by the portable layer each time a tick interrupt occurs.

     * Increments the tick then checks to see if the new tick value will cause any

     * tasks to be unblocked. */

    traceTASK_INCREMENT_TICK( xTickCount );



    /* Tick increment should occur on every kernel timer event. Core 0 has the

     * responsibility to increment the tick, or increment the pended ticks if the

     * scheduler is suspended.  If pended ticks is greater than zero, the core that

     * calls xTaskResumeAll has the responsibility to increment the tick. */

    if( uxSchedulerSuspended == ( UBaseType_t ) 0U )

    {

        /* Minor optimisation.  The tick count cannot change in this

         * block. */

        const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1;



        /* Increment the RTOS tick, switching the delayed and overflowed

         * delayed lists if it wraps to 0. */

        xTickCount = xConstTickCount;



        if( xConstTickCount == ( TickType_t ) 0U ) // 若为0则说明时钟计数器发生了溢出

        {

            taskSWITCH_DELAYED_LISTS(); // 交换延时任务的列表

        }

        else

        {

            mtCOVERAGE_TEST_MARKER();

        }



        /* See if this tick has made a timeout expire.  Tasks are stored in

         * the  queue in the order of their wake time - meaning once one task

         * has been found whose block time has not expired there is no need to

         * look any further down the list. */

        if( xConstTickCount >= xNextTaskUnblockTime )

        {

            for( ; ; )

            {

                if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )

                {

                    /* The delayed list is empty.  Set xNextTaskUnblockTime

                     * to the maximum possible value so it is extremely

                     * unlikely that the

                     * if( xTickCount >= xNextTaskUnblockTime ) test will pass

                     * next time through. */

                    xNextTaskUnblockTime = portMAX_DELAY;

                    break;

                }

                else

                {

                    /* The delayed list is not empty, get the value of the

                     * item at the head of the delayed list.  This is the time

                     * at which the task at the head of the delayed list must

                     * be removed from the Blocked state. */

                    /* MISRA Ref 11.5.3 [Void pointer assignment] */

                    /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */

                    /* coverity[misra_c_2012_rule_11_5_violation] */

                    pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );

                    xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );



                    if( xConstTickCount < xItemValue )

                    {

                        /* It is not time to unblock this item yet, but the

                         * item value is the time at which the task at the head

                         * of the blocked list must be removed from the Blocked

                         * state -  so record the item value in

                         * xNextTaskUnblockTime. */

                        xNextTaskUnblockTime = xItemValue;

                        break;

                    }

                    else

                    {

                        mtCOVERAGE_TEST_MARKER();

                    }



                    /* It is time to remove the item from the Blocked state. */

                    listREMOVE_ITEM( &( pxTCB->xStateListItem ) );



                    /* Is the task waiting on an event also?  If so remove

                     * it from the event list. */

                    if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )

                    {

                        listREMOVE_ITEM( &( pxTCB->xEventListItem ) );

                    }

                    else

                    {

                        mtCOVERAGE_TEST_MARKER();

                    }



                    /* Place the unblocked task into the appropriate ready

                     * list. */

                    prvAddTaskToReadyList( pxTCB );



                    /* A task being unblocked cannot cause an immediate

                     * context switch if preemption is turned off. */

                    #if ( configUSE_PREEMPTION == 1 )

                    {

                        #if ( configNUMBER_OF_CORES == 1 )

                        {

                            /* Preemption is on, but a context switch should

                             * only be performed if the unblocked task's

                             * priority is higher than the currently executing

                             * task.

                             * The case of equal priority tasks sharing

                             * processing time (which happens when both

                             * preemption and time slicing are on) is

                             * handled below.*/

                            if( pxTCB->uxPriority > pxCurrentTCB->uxPriority )

                            {

                                xSwitchRequired = pdTRUE;

                            }

                            else

                            {

                                mtCOVERAGE_TEST_MARKER();

                            }

                        }

                        #else /* #if( configNUMBER_OF_CORES == 1 ) */

                        {

                            prvYieldForTask( pxTCB );

                        }

                        #endif /* #if( configNUMBER_OF_CORES == 1 ) */

                    }

                    #endif /* #if ( configUSE_PREEMPTION == 1 ) */

                }

            }

        }



        /* Tasks of equal priority to the currently running task will share

         * processing time (time slice) if preemption is on, and the application

         * writer has not explicitly turned time slicing off. */

        #if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) )

        {

            #if ( configNUMBER_OF_CORES == 1 )

            {

                if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > 1U )

                {

                    xSwitchRequired = pdTRUE;

                }

                else

                {

                    mtCOVERAGE_TEST_MARKER();

                }

            }

            #else /* #if ( configNUMBER_OF_CORES == 1 ) */

            {

                BaseType_t xCoreID;



                for( xCoreID = 0; xCoreID < ( ( BaseType_t ) configNUMBER_OF_CORES ); xCoreID++ )

                {

                    if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCBs[ xCoreID ]->uxPriority ] ) ) > 1U )

                    {

                        xYieldPendings[ xCoreID ] = pdTRUE;

                    }

                    else

                    {

                        mtCOVERAGE_TEST_MARKER();

                    }

                }

            }

            #endif /* #if ( configNUMBER_OF_CORES == 1 ) */

        }

        #endif /* #if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */



        #if ( configUSE_TICK_HOOK == 1 )

        {

            /* Guard against the tick hook being called when the pended tick

             * count is being unwound (when the scheduler is being unlocked). */

            if( xPendedTicks == ( TickType_t ) 0 )

            {

                vApplicationTickHook();

            }

            else

            {

                mtCOVERAGE_TEST_MARKER();

            }

        }

        #endif /* configUSE_TICK_HOOK */



        #if ( configUSE_PREEMPTION == 1 )

        {

            #if ( configNUMBER_OF_CORES == 1 )

            {

                /* For single core the core ID is always 0. */

                if( xYieldPendings[ 0 ] != pdFALSE )

                {

                    xSwitchRequired = pdTRUE;

                }

                else

                {

                    mtCOVERAGE_TEST_MARKER();

                }

            }

            #else /* #if ( configNUMBER_OF_CORES == 1 ) */

            {

                BaseType_t xCoreID, xCurrentCoreID;

                xCurrentCoreID = ( BaseType_t ) portGET_CORE_ID();



                for( xCoreID = 0; xCoreID < ( BaseType_t ) configNUMBER_OF_CORES; xCoreID++ )

                {

                    #if ( configUSE_TASK_PREEMPTION_DISABLE == 1 )

                        if( pxCurrentTCBs[ xCoreID ]->xPreemptionDisable == pdFALSE )

                    #endif

                    {

                        if( xYieldPendings[ xCoreID ] != pdFALSE )

                        {

                            if( xCoreID == xCurrentCoreID )

                            {

                                xSwitchRequired = pdTRUE;

                            }

                            else

                            {

                                prvYieldCore( xCoreID );

                            }

                        }

                        else

                        {

                            mtCOVERAGE_TEST_MARKER();

                        }

                    }

                }

            }

            #endif /* #if ( configNUMBER_OF_CORES == 1 ) */

        }

        #endif /* #if ( configUSE_PREEMPTION == 1 ) */

    }

    else

    {

        xPendedTicks += 1U;



        /* The tick hook gets called at regular intervals, even if the

         * scheduler is locked. */

        #if ( configUSE_TICK_HOOK == 1 )

        {

            vApplicationTickHook();

        }

        #endif

    }



    traceRETURN_xTaskIncrementTick( xSwitchRequired );



    return xSwitchRequired;

}

参考文献

《Cortex-M4 Devices Generic User Guid》