FreeRTOS 6:任务创建函数xTaskCreate分析

动态创建任务xTaskCreate

xTaskCreate

1、申请堆栈内存(返回首地址)

2、申请任务控制块内存(返回首地址)

3、把前面申请的堆栈地址,赋值给控制块的堆栈成员

4、调用prvlnitialiseNewTask初始化任务控制块中的成员

5、调用prvAddNewTaskToReadyList添加新创建任务到就绪列表中

/**
 * 创建一个任务并将其添加到就绪列表中。
 * 
 * @param pxTaskCode 任务的入口函数。
 * @param pcName 任务的名称,主要用于调试。
 * @param usStackDepth 任务堆栈的大小,单位是堆栈项的数量。
 * @param pvParameters 传递给任务入口函数的参数。
 * @param uxPriority 任务的优先级,优先级越高,任务的优先级越高。
 * @param pxCreatedTask 用于存储新创建任务的句柄的变量的地址,如果不需要句柄,可以设置为NULL。
 * 
 * @return 返回任务创建的结果,pdPASS表示成功,errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY表示由于缺乏内存而失败。
 * 
 * 此函数根据指定的参数创建一个新的任务,并将其添加到就绪列表中。如果内存不足,将返回错误代码。
 * 它首先根据堆栈增长的方向分配TCB(任务控制块)和堆栈空间,然后初始化新任务并将其添加到就绪列表中。
 */
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
                        const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
                        const configSTACK_DEPTH_TYPE usStackDepth,
                        void * const pvParameters,
                        UBaseType_t uxPriority,
                        TaskHandle_t * const pxCreatedTask )
{
    TCB_t * pxNewTCB;
    BaseType_t xReturn;

    /* 如果堆栈向下增长,则先分配堆栈,然后分配TCB,以防止堆栈增长到TCB中。
     * 同样,如果堆栈向上增长,则先分配TCB,然后分配堆栈。 */
    #if ( portSTACK_GROWTH > 0 )
        {
            /* 分配TCB的空间。内存的来源取决于port malloc函数的实现以及是否使用静态分配。 */
            pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );

            if( pxNewTCB != NULL )
            {
                /* 为新创建的任务分配堆栈空间。
                 * 堆栈的基地址存储在TCB中,以便在需要时删除任务。 */
                pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */

                if( pxNewTCB->pxStack == NULL )
                {
                    /* 无法分配堆栈。删除已分配的TCB。 */
                    vPortFree( pxNewTCB );
                    pxNewTCB = NULL;
                }
            }
        }
    #else /* portSTACK_GROWTH */
        {
            StackType_t * pxStack;

            /* 为新创建的任务分配堆栈空间。 */
            pxStack = pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation is the stack. */

            if( pxStack != NULL )
            {
                /* 分配TCB的空间。 */
                pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e9087 !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack, and the first member of TCB_t is always a pointer to the task's stack. */

                if( pxNewTCB != NULL )
                {
                    /* 将堆栈位置存储在TCB中。 */
                    pxNewTCB->pxStack = pxStack;
                }
                else
                {
                    /* 由于TCB未创建,堆栈无法使用。再次释放它。 */
                    vPortFree( pxStack );
                }
            }
            else
            {
                pxNewTCB = NULL;
            }
        }
    #endif /* portSTACK_GROWTH */

    if( pxNewTCB != NULL )
    {
        #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e9029 !e731 Macro has been consolidated for readability reasons. */
            {
                /* 可以静态或动态创建任务,因此记录此任务是动态创建的,以防它稍后被删除。 */
                pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;
            }
        #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */

        /* 初始化新任务并将其添加到就绪列表中。 */
        prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
        prvAddNewTaskToReadyList( pxNewTCB );
        xReturn = pdPASS;
    }
    else
    {
        /* 由于缺乏内存,任务创建失败。 */
        xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
    }

    /* 返回任务创建的结果。 */
    return xReturn;
}

prvlnitialiseNewTask

xTaskCreate

prvlnitialiseNewTask

1、初始化堆栈为Oxa5(可选)

2、记录栈顶,保存在pxTopOfStack

3、保存任务名字到pxNewTCP>PCTasKname[ x ]中

4、保存任务优先级到p×NewTCB->uxPriority

5、设置状态列表项的所属控制块,设置事件列表项的优先级

6、列表项的擦汗如入是从小到大插入,所以这里将越高优先级的任务的事件列表项值设置越小,这样就可以排前面

7、调用pxPortlnitialiseStack初始化任务堆栈,用于保存当前任务上下文器信息,已备后续任务切换使用

8.将任务句柄等于控制块

static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
                                  const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
                                  const uint32_t ulStackDepth,
                                  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 )
        /* 是否需要以特权模式创建任务? */
        BaseType_t xRunPrivileged;

        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 )
        {
            /* 使用已知值填充堆栈,以便于调试 */
            ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) );
        }
    #endif /* tskSET_NEW_STACKS_TO_KNOWN_VALUE */
    /* 计算栈顶地址。这取决于栈是从高内存向低内存增长(如80x86架构)还是相反。
    * portSTACK_GROWTH 用于根据端口需求使结果为正或负。 */
    #if ( portSTACK_GROWTH < 0 )
        {
            /* 计算从高到低增长的栈的栈顶地址。 */
            pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] );
            
            /* 将栈顶地址对齐到所需的字节边界。 */
            pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 !e9033 !e9078 MISRA 异常。避免指针和整数之间的转换在实际中不可行。使用 portPOINTER_SIZE_TYPE 类型来处理大小差异。通过 assert() 检查。 */

            /* 检查计算出的栈顶地址对齐是否正确。 */
            configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );

            #if ( configRECORD_STACK_HIGH_ADDRESS == 1 )
                {
                    /* 同时记录栈的高地址,这可能有助于调试。 */
                    pxNewTCB->pxEndOfStack = pxTopOfStack;
                }
            #endif /* configRECORD_STACK_HIGH_ADDRESS */
        }
    #else /* portSTACK_GROWTH */
        {
            /* 计算从低到高增长的栈的栈顶地址。 */
            pxTopOfStack = pxNewTCB->pxStack;

            /* 检查栈缓冲区的对齐是否正确。 */
            configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );

            /* 如果进行栈检查,则需要栈空间的另一端。 */
            pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
        }
    #endif /* portSTACK_GROWTH */

    /* 将任务名称存储在TCB中。 */
    if( pcName != NULL )
    {
        for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
        {
            pxNewTCB->pcTaskName[ x ] = pcName[ x ];

            /* 如果字符串长度小于 configMAX_TASK_NAME_LEN,确保不会复制超过实际字符串长度的部分,
             * 以防字符串后面的内存不可访问(极不可能发生)。 */
            if( pcName[ x ] == ( char ) 0x00 )
            {
                break;
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }

        /* 确保在字符串长度大于或等于 configMAX_TASK_NAME_LEN 时,字符串以空字符终止。 */
        pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';
    }
    else
    {
        /* 如果任务没有被赋予名称,确保读取时有一个空终止符。 */
        pxNewTCB->pcTaskName[ 0 ] = 0x00;
    }

    /* 用作数组索引,因此必须确保它不会太大。首先移除特权位(如果存在的话)。 */
    if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
    {
        /* 如果优先级超出最大值,将其设置为最大优先级减一。 */
        uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

    /* 将优先级存储在新创建的TCB中。 */
    pxNewTCB->uxPriority = uxPriority;

    #if ( configUSE_MUTEXES == 1 )
        {
            /* 如果配置支持互斥锁,初始化基优先级和持有的互斥锁数量。 */
            pxNewTCB->uxBasePriority = uxPriority;
            pxNewTCB->uxMutexesHeld = 0;
        }
    #endif /* configUSE_MUTEXES */

    /* 初始化新创建的TCB中的状态列表项。 */
    vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
    /* 初始化新创建的TCB中的事件列表项。 */
    vListInitialiseItem( &( pxNewTCB->xEventListItem ) );

    /* 将 pxNewTCB 设置为状态列表项的拥有者。这样可以从列表中的任意项返回到包含它的 TCB。 */
    listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );

    /* 事件列表总是按优先级顺序排列。 */
    listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); /*lint !e961 MISRA 异常,因为对于某些端口,这些类型转换是多余的。*/

    /* 将 pxNewTCB 设置为事件列表项的拥有者。 */
    listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB );

     #if ( portCRITICAL_NESTING_IN_TCB == 1 )
        {
            /* 如果配置了在 TCB 中管理临界区嵌套计数,则初始化为 0。 */
            pxNewTCB->uxCriticalNesting = ( UBaseType_t ) 0U;
        }
    #endif /* portCRITICAL_NESTING_IN_TCB */

    #if ( configUSE_APPLICATION_TASK_TAG == 1 )
        {
            /* 如果配置了应用任务标签,则初始化为 NULL。 */
            pxNewTCB->pxTaskTag = NULL;
        }
    #endif /* configUSE_APPLICATION_TASK_TAG */

    #if ( configGENERATE_RUN_TIME_STATS == 1 )
        {
            /* 如果配置了生成运行时统计信息,则初始化运行时间计数器为 0。 */
            pxNewTCB->ulRunTimeCounter = 0UL;
        }
    #endif /* configGENERATE_RUN_TIME_STATS */

    #if ( portUSING_MPU_WRAPPERS == 1 )
        {
            /* 如果使用 MPU 包装器,则存储任务的 MPU 设置。 */
            vPortStoreTaskMPUSettings( &( pxNewTCB->xMPUSettings ), xRegions, pxNewTCB->pxStack, ulStackDepth );
        }
    #else
        {
            /* 避免编译器关于未引用参数的警告。 */
            ( void ) xRegions;
        }
    #endif

    #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 )
        {
            /* 如果配置了线程本地存储指针,则初始化为 0。 */
            memset( ( void * ) &( pxNewTCB->pvThreadLocalStoragePointers[ 0 ] ), 0x00, sizeof( pxNewTCB->pvThreadLocalStoragePointers ) );
        }
    #endif

    #if ( configUSE_TASK_NOTIFICATIONS == 1 )
        {
            /* 如果配置了任务通知,则初始化通知值和状态为 0。 */
            memset( ( void * ) &( pxNewTCB->ulNotifiedValue[ 0 ] ), 0x00, sizeof( pxNewTCB->ulNotifiedValue ) );
            memset( ( void * ) &( pxNewTCB->ucNotifyState[ 0 ] ), 0x00, sizeof( pxNewTCB->ucNotifyState ) );
        }
    #endif

    #if ( configUSE_NEWLIB_REENTRANT == 1 )
        {
            /* 初始化此任务的 Newlib 重入结构。
             * 有关更多信息,请参阅第三方链接 http://www.nadler.com/embedded/newlibAndFreeRTOS.html。 */
            _REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) );
        }
    #endif

    #if ( INCLUDE_xTaskAbortDelay == 1 )
        {
            /* 如果配置了任务延迟中止功能,则初始化延迟中止标志为 false。 */
            pxNewTCB->ucDelayAborted = pdFALSE;
        }
    #endif

    /* 初始化 TCB 栈,使其看起来像是任务已经在运行,但被调度器中断了一样。
     * 返回地址设置为任务函数的起始地址。一旦栈初始化完成,更新栈顶变量。 */
    #if ( portUSING_MPU_WRAPPERS == 1 )
        {
            /* 如果端口具有检测栈溢出的能力,则将栈结束地址传递给栈初始化函数。 */
            #if ( portHAS_STACK_OVERFLOW_CHECKING == 1 )
                {
                    #if ( portSTACK_GROWTH < 0 )
                        {
                            /* 栈从高地址向低地址增长。 */
                            pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters, xRunPrivileged );
                        }
                    #else /* portSTACK_GROWTH */
                        {
                            /* 栈从低地址向高地址增长。 */
                            pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters, xRunPrivileged );
                        }
                    #endif /* portSTACK_GROWTH */
                }
            #else /* portHAS_STACK_OVERFLOW_CHECKING */
                {
                    /* 端口不支持栈溢出检测。 */
                    pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged );
                }
            #endif /* portHAS_STACK_OVERFLOW_CHECKING */
        }
    #else /* portUSING_MPU_WRAPPERS */
        {
            /* 如果端口具有检测栈溢出的能力,则将栈结束地址传递给栈初始化函数。 */
            #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 */

    if( pxCreatedTask != NULL )
    {
        /* 以匿名方式传递任务句柄。任务句柄可以用于更改创建的任务的优先级、删除任务等。 */
        *pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
    }
    else
    {
        /* 覆盖测试标记,用于确保所有代码路径都被测试到。 */
        mtCOVERAGE_TEST_MARKER();
    }
}
pxPortlnitialiseStack

xTaskCreate

prvlnitialiseNewTask

pxPortlnitialiseS

        函数 pxPortInitialiseStack()用于初始化任务栈,就是往任务的栈中写入一些重要的信息,这些信息会在任务切换的时候被弹出到 CPU 寄存器中,以恢复任务的上下文信息,这些信息就包括 xPSR 寄存器的初始值、 任务的函数地址(PC 寄存器)、任务错误退出函数地址(LR 寄存器)、任务函数的传入参数(R0 寄存器)以及为 R1~R12 寄存器预留空间,若使用了浮点单元,那么还会有 EXC_RETURN 的值。同时该函数会返回更新后的栈顶指针。
        针 对 ARM Cortex-M3 和 针 对 ARM Cortex-M4 和 ARM Cortex-M7 内 核 的 函 数pxPortInitialiseStack()稍有不同, 原因在于 ARM Cortex-M4 和 ARM Cortex-M7 内核具有浮点单元, 因此在任务栈中还需保存浮点寄存器的值。
        针对 ARM Cortex-M3 内核的函数 pxPortInitialiseStack(), 具体的代码如下所示:

StackType_t * pxPortInitialiseStack(
		StackType_t * pxTopOfStack, /* 任务栈顶指针 */
			TaskFunction_t pxCode, /* 任务函数地址 */
				void * pvParameters) /* 任务函数传入参数 */
{
	/* 模拟栈的格式将信息保存到任务栈中,用于上下文切换 */
	pxTopOfStack--;

	/* xPSR 寄存器初始值为 0x01000000 */
	*pxTopOfStack = portINITIAL_XPSR;
	pxTopOfStack--;

	/* 任务函数的地址(PC 寄存器) */
	*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK;
	pxTopOfStack--;


	/* 任务错误退出函数地址(LR 寄存器) */
	*pxTopOfStack = ( StackType_t ) prvTaskExitError;

	/* 为 R12、 R3、 R2、 R1 寄存器预留空间 */
	pxTopOfStack -= 5;

	/* 任务函数的传入参数(R0 寄存器) */
	*pxTopOfStack = ( StackType_t ) pvParameters;

	/* 为 R11、 R10、 R9、 R8、 R7、 R6、 R5、 R4 寄存器预留空间 */
	pxTopOfStack -= 8;

	/* 返回更新后的任务栈指针
	 * 后续任务运行时需要用到栈的地方,
	 * 将从这个地址开始保存信息
	 */
	return pxTopOfStack;
}

StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
                                     TaskFunction_t pxCode,
                                     void * pvParameters )
{
    /* 模拟上下文切换中断创建的堆栈帧。 */
    pxTopOfStack--;                                                      /* 增加偏移量以适应 MCU 在中断进入/退出时对堆栈的使用方式。 */
    *pxTopOfStack = portINITIAL_XPSR;                                    /* xPSR 寄存器的初始值 */
    pxTopOfStack--;
    *pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC 寄存器的初始值,即任务函数的入口地址 */
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) prvTaskExitError;                    /* LR 寄存器的初始值,用于错误处理 */

    pxTopOfStack -= 5;                                                   /* 为 R12, R3, R2 和 R1 预留空间 */
    *pxTopOfStack = ( StackType_t ) pvParameters;                        /* R0 寄存器的初始值,即任务参数 */
    pxTopOfStack -= 8;                                                   /* 为 R11, R10, R9, R8, R7, R6, R5 和 R4 预留空间 */

    return pxTopOfStack;                                                 /* 返回调整后的堆栈顶部指针 */
}

针对 ARM Cortex-M4 和 ARM Cortex-M7 内核的函数 pxPortInitialiseStack(),具体的代码如下所示:

StackType_t * pxPortInitialiseStack(
	StackType_t * pxTopOfStack, /* 任务栈顶指针 */
		TaskFunction_t pxCode, /* 任务函数地址 */
			void * pvParameters) /* 任务函数传入参数 */
{
	/* 模拟栈的格式将信息保存到任务栈中,用于上下文切换 */
	pxTopOfStack--;
	/* xPSR 寄存器初始值为 0x01000000 */
	*pxTopOfStack = portINITIAL_XPSR;
	pxTopOfStack--;
	/* 任务函数的地址(PC 寄存器) */
	*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK;
	pxTopOfStack--;
	/* 任务错误退出函数地址(LR 寄存器) */
	*pxTopOfStack = ( StackType_t ) prvTaskExitError;
	/* 为 R12、 R3、 R2、 R1 寄存器预留空间 */
	pxTopOfStack -= 5;
	/* 任务函数的传入参数(R0 寄存器) */
	*pxTopOfStack = ( StackType_t ) pvParameters;
	pxTopOfStack--;
	/* EXC_RETURN
	* 初始化为 0xFFFFFFFD,
	* 即表示不使用浮点单元,且中断返回后进入线程模式,使用 PSP
	*/
	*pxTopOfStack = portINITIAL_EXC_RETURN;
	/* 为 R11、 R10、 R9、 R8、 R7、 R6、 R5、 R4 寄存器预留空间 */
	pxTopOfStack -= 8;
	/* 返回更新后的任务栈指针
	* 后续任务运行时需要用到栈的地方,
	* 将从这个地址开始保存信息
	*/
	return pxTopOfStack;
}

函数 pxPortInitialiseStack()初始化后的任务栈如下图所示:

prvAddNewTaskToReadyList

xTaskCreate

prvAddNewTaskToReadyList

1、记录任务数量uxCurrentNumberOfTasks++

2、判断新创建的任务是否为第一个任务

如果创建的是第一个任务,初始化任务列表prvInitialiseTaskLists;如果创建的不是第一个任务,并且调度器还未开始启动,比较新任务与正在执行的任务优先级大小,新任务优先级大的话,将当前控制块重新指向新的控制块

3、将新的任务控制块添加到就绪列表中,使用函数prvAddTaskToReadyList

4、如果调度器已经开始运行,并且新任务的优先级更大的话,进行一次任务切换

将uxTopReadyPrioritytä应bit置一,表示相应优先级有就绪任务,比如任务优先级为5,就该变量的位5置一,方便后续任务切换判断,对应的就绪列表是否有任务存在将新创建的任务插入对应的就绪列表末尾

static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
{
    /* 确保在更新任务列表时中断不会访问任务列表。 */
    taskENTER_CRITICAL();
    {
        /* 增加当前任务数。 */
        uxCurrentNumberOfTasks++;

        if( pxCurrentTCB == NULL )
        {
            /* 没有其他任务,或者所有其他任务都处于挂起状态,将新任务设为当前任务。 */
            pxCurrentTCB = pxNewTCB;

            if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
            {
                /* 这是创建的第一个任务,进行必要的初步初始化。如果此调用失败,我们无法恢复,但会报告失败。 */
                prvInitialiseTaskLists();
            }
            else
            {
                /* 覆盖测试标记,用于确保所有代码路径都被测试到。 */
                mtCOVERAGE_TEST_MARKER();
            }
        }
        else
        {
            /* 如果调度器尚未运行,且新任务的优先级高于当前任务,则将新任务设为当前任务。 */
            if( xSchedulerRunning == pdFALSE )
            {
                if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
                {
                    pxCurrentTCB = pxNewTCB;
                }
                else
                {
                    /* 覆盖测试标记,用于确保所有代码路径都被测试到。 */
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            else
            {
                /* 覆盖测试标记,用于确保所有代码路径都被测试到。 */
                mtCOVERAGE_TEST_MARKER();
            }
        }

        /* 增加任务编号。 */
        uxTaskNumber++;

        #if ( configUSE_TRACE_FACILITY == 1 )
            {
                /* 仅用于跟踪,向 TCB 添加一个计数器。 */
                pxNewTCB->uxTCBNumber = uxTaskNumber;
            }
        #endif /* configUSE_TRACE_FACILITY */

        /* 记录任务创建。 */
        traceTASK_CREATE( pxNewTCB );

        /* 将新任务添加到就绪列表。 */
        prvAddTaskToReadyList( pxNewTCB );

        /* 设置 TCB。 */
        portSETUP_TCB( pxNewTCB );
    }
    taskEXIT_CRITICAL();

    if( xSchedulerRunning != pdFALSE )
    {
        /* 如果新创建的任务优先级高于当前任务,则应立即运行新任务。 */
        if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority )
        {
            taskYIELD_IF_USING_PREEMPTION();
        }
        else
        {
            /* 覆盖测试标记,用于确保所有代码路径都被测试到。 */
            mtCOVERAGE_TEST_MARKER();
        }
    }
    else
    {
        /* 覆盖测试标记,用于确保所有代码路径都被测试到。 */
        mtCOVERAGE_TEST_MARKER();
    }
}
prvAddTaskToReadyList( pxTCB )

/*
 * Place the task represented by pxTCB into the appropriate ready list for
 * the task.  It is inserted at the end of the list.
 */
#define prvAddTaskToReadyList( pxTCB )                                                                 \
    traceMOVED_TASK_TO_READY_STATE( pxTCB );                                                           \
    taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority );                                                \
    vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \
    tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )

/**
 * 将新列表项插入到列表末尾。
 * 
 * 此函数用于将新项添加到指定列表的末尾。它不会对列表进行排序,而是将新添加的项设置为最后一个被检索的项。
 * 该函数适用于特定的使用场景和列表管理需求。
 * 
 * @param pxList 指向要添加新项的列表的指针。
 * @param pxNewListItem 指向要添加的新项的指针。
 */
void vListInsertEnd( List_t * const pxList,
                     ListItem_t * const pxNewListItem )
{
    // 获取列表的索引项,用于遍历和操作列表。
    ListItem_t * const pxIndex = pxList->pxIndex;

    // 仅在定义了 configASSERT() 时有效,这些测试可以捕获内存中被覆盖的列表数据结构问题。
    // 它们不会捕获因 FreeRTOS 配置或使用不当导致的数据错误。
    listTEST_LIST_INTEGRITY( pxList );
    listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );

    // 将新列表项插入到 pxList 中,但不对其进行排序,
    // 而是将新列表项设置为通过调用 listGET_OWNER_OF_NEXT_ENTRY() 最后移除的项。
    pxNewListItem->pxNext = pxIndex;
    pxNewListItem->pxPrevious = pxIndex->pxPrevious;

    // 仅在决策覆盖率测试中使用。
    mtCOVERAGE_TEST_DELAY();

    // 更新列表项之间的链接,以正确插入新项。
    pxIndex->pxPrevious->pxNext = pxNewListItem;
    pxIndex->pxPrevious = pxNewListItem;

    // 记录该项所属的列表。
    pxNewListItem->pxContainer = pxList;

    // 增加列表中的项数。
    ( pxList->uxNumberOfItems )++;
}

此函数就是将待插入的列表项插入到列表 pxIndex 指向列表项的前面,要注意的时, pxIndex 不一定指向 xListEnd,而是有可能指向列表中任意一个列表项。函数 vListInsertEnd()插入列表项后的列表结构示意图,如下图所示:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苦梨甜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值