【freertos-kernel】queue(创建)


边学边记,朋友们多多指教。

补充

补充一些东西。

任务进出临界区

临界区(Critical Section) 是一段不允许被中断的代码区域。

//task.h
#define taskENTER_CRITICAL()                 portENTER_CRITICAL()
#if ( configNUMBER_OF_CORES == 1 )
    #define taskENTER_CRITICAL_FROM_ISR()    portSET_INTERRUPT_MASK_FROM_ISR()
#else
    #define taskENTER_CRITICAL_FROM_ISR()    portENTER_CRITICAL_FROM_ISR()
#endif

#define taskEXIT_CRITICAL()                    portEXIT_CRITICAL()
#if ( configNUMBER_OF_CORES == 1 )
    #define taskEXIT_CRITICAL_FROM_ISR( x )    portCLEAR_INTERRUPT_MASK_FROM_ISR( x )
#else
    #define taskEXIT_CRITICAL_FROM_ISR( x )    portEXIT_CRITICAL_FROM_ISR( x )
#endif

以gcc/arm-cm3(单核)为例,多核的临界区进出更复杂,除了要关闭中断,还要防止不同 CPU 同时修改共享数据。

ISR临界区(中断服务函数中使用)

ARM Cortex-M 系列提供了一个叫 BASEPRI 的寄存器,用于控制中断屏蔽级别,只有比他优先级更低(优先级号更大)的中断才会响应。

//portmacro.h
#define portSET_INTERRUPT_MASK_FROM_ISR()         ulPortRaiseBASEPRI()
//port.c
//BASEPRI设置为configMAX_SYSCALL_INTERRUPT_PRIORITY屏蔽中断响应,返回原BASEPRI值,方便后面恢复
portFORCE_INLINE static uint32_t ulPortRaiseBASEPRI( void )
{
    uint32_t ulOriginalBASEPRI, ulNewBASEPRI;
    __asm volatile
    (
        "   mrs %0, basepri \n" \ //将当前 BASEPRI 寄存器的值读入变量 ulOriginalBASEPRI
        "   mov %1, %2 \n" \ //把configMAX_SYSCALL_INTERRUPT_PRIORITY(对应 %2)赋值给寄存器 %1(即 ulNewBASEPRI)
        "   msr basepri, %1 \n" \ //将新的优先级值写入 BASEPRI 寄存器,开始屏蔽中断
        "   isb \n" \
        "   dsb \n" \
        : "=r" ( ulOriginalBASEPRI ), "=r" ( ulNewBASEPRI ) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : "memory"
    );
    return ulOriginalBASEPRI;
}

//portmacro.h
#define portCLEAR_INTERRUPT_MASK_FROM_ISR( x )    vPortSetBASEPRI( x )
//port.c
//设置中断屏蔽等级ulNewMaskValue
portFORCE_INLINE static void vPortSetBASEPRI( uint32_t ulNewMaskValue )
{
    __asm volatile
    (
        "   msr basepri, %0 " ::"r" ( ulNewMaskValue ) : "memory"
    );
}

普通临界区(任务中使用)

#define portDISABLE_INTERRUPTS()                  vPortRaiseBASEPRI()
#define portENABLE_INTERRUPTS()                   vPortSetBASEPRI( 0 )

使能中断就是把basepri设为0(优先级最高),
失能中断就是把basepri设为configMAX_SYSCALL_INTERRUPT_PRIORITY (优先级最低)。

//portmacro.h
#define portENTER_CRITICAL()                      vPortEnterCritical()
#define portEXIT_CRITICAL()                       vPortExitCritical()

//port.c
void vPortEnterCritical( void )
{
    portDISABLE_INTERRUPTS();
    uxCriticalNesting++;
    if( uxCriticalNesting == 1 )
    {
    	//表示当前不是在中断服务程序中执行
        configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
    }
}
void vPortExitCritical( void )
{
    configASSERT( uxCriticalNesting );
    uxCriticalNesting--;
    if( uxCriticalNesting == 0 )
    {
        portENABLE_INTERRUPTS();
    }
}

uxCriticalNesting是一个全局变量,记录当前任务进入临界区的嵌套次数。
ISR临界区没有中断嵌套计数。

xTaskRemoveFromEventList

从一个事件列表中移除一个任务,并根据情况将其添加到就绪列表,从而实现任务的唤醒机制。

BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList )
{
    TCB_t * pxUnblockedTCB;
    BaseType_t xReturn;
    traceENTER_xTaskRemoveFromEventList( pxEventList );
    pxUnblockedTCB = listGET_OWNER_OF_HEAD_ENTRY( pxEventList );
    configASSERT( pxUnblockedTCB );
    listREMOVE_ITEM( &( pxUnblockedTCB->xEventListItem ) );////移除pxEventList的第一个任务(优先级最高)
    if( uxSchedulerSuspended == ( UBaseType_t ) 0U )	//如果调度器没有被挂起
    {
        listREMOVE_ITEM( &( pxUnblockedTCB->xStateListItem ) );//把任务从原来的状态链表中移除
        prvAddTaskToReadyList( pxUnblockedTCB );//把它加入就绪链表
        #if ( configUSE_TICKLESS_IDLE != 0 )
        {
            prvResetNextTaskUnblockTime();//更新下一个要唤醒的时间点
        }
        #endif
    }
    else	// 如果调度器被挂起,暂存到 xPendingReadyList
    {
        listINSERT_END( &( xPendingReadyList ), &( pxUnblockedTCB->xEventListItem ) );
    }
    #if ( configNUMBER_OF_CORES == 1 )
    {
        if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority )
        {//如果被唤醒的任务优先级高于当前正在运行的任务
            xReturn = pdTRUE;
            xYieldPendings[ 0 ] = pdTRUE;//设置 xYieldPendings[0] 标志,表示需要调度
        }
        else
        {
            xReturn = pdFALSE;
        }
    }
    #else /* #if ( configNUMBER_OF_CORES == 1 ) */
    {
        xReturn = pdFALSE;
        #if ( configUSE_PREEMPTION == 1 )
        {
            prvYieldForTask( pxUnblockedTCB );//判断是否需要触发调度
            if( xYieldPendings[ portGET_CORE_ID() ] != pdFALSE )
            {
                xReturn = pdTRUE;
            }
        }
        #endif /* #if ( configUSE_PREEMPTION == 1 ) */
    }
    #endif /* #if ( configNUMBER_OF_CORES == 1 ) */
    traceRETURN_xTaskRemoveFromEventList( xReturn );
    return xReturn;
}

prvResetNextTaskUnblockTime

更新系统变量 xNextTaskUnblockTime,记录下一个要被唤醒的任务的时间点 ,以便在进入Tickless Idle时知道最多可以休眠多久。

static void prvResetNextTaskUnblockTime( void )
{
    if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )//没有任何任务在等待延时唤醒
    {
        xNextTaskUnblockTime = portMAX_DELAY;
    }
    else	//如果链表不为空,则获取第一个任务的唤醒时间
    {
        xNextTaskUnblockTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxDelayedTaskList );
    }
}

prvYieldForTask

根据一个被唤醒的任务的优先级,判断是否需要在某个 CPU 核心上触发一次上下文切换(yield),以确保高优先级任务能够尽快运行。

static void prvYieldForTask( const TCB_t * pxTCB )
{
    BaseType_t xLowestPriorityToPreempt;
    BaseType_t xCurrentCoreTaskPriority;
    BaseType_t xLowestPriorityCore = ( BaseType_t ) -1;
    BaseType_t xCoreID;
    #if ( configRUN_MULTIPLE_PRIORITIES == 0 )
        BaseType_t xYieldCount = 0;
    #endif /* #if ( configRUN_MULTIPLE_PRIORITIES == 0 ) */
    configASSERT( portGET_CRITICAL_NESTING_COUNT() > 0U );//必须在临界区中调用这个函数
    #if ( configRUN_MULTIPLE_PRIORITIES == 0 )//如果只运行单一优先级的任务
        if( pxTCB->uxPriority >= uxTopReadyPriority )//只有当新唤醒任务优先级 ≥ 当前最高就绪优先级时才处理
    #else//正常多优先级系统
        if( taskTASK_IS_RUNNING( pxTCB ) == pdFALSE )//如果该任务不在运行态,才考虑抢占
    #endif
    {
        xLowestPriorityToPreempt = ( BaseType_t ) pxTCB->uxPriority;
        --xLowestPriorityToPreempt;
        //遍历所有核心
        for( xCoreID = ( BaseType_t ) 0; xCoreID < ( BaseType_t ) configNUMBER_OF_CORES; xCoreID++ )
        {
        	//获取当前核心任务优先级
            xCurrentCoreTaskPriority = ( BaseType_t ) pxCurrentTCBs[ xCoreID ]->uxPriority;
            if( ( pxCurrentTCBs[ xCoreID ]->uxTaskAttributes & taskATTRIBUTE_IS_IDLE ) != 0U )
            {
                xCurrentCoreTaskPriority = ( BaseType_t ) ( xCurrentCoreTaskPriority - 1 );
            }
            //检查是否可以抢占,当前核心上有任务在运行,并且还没有 pending yield
            //如果当前核心运行任务优先级 ≤ 被唤醒任务;
			//并且符合亲和性设置;
			//并且没有禁止抢占;
			//则更新为最合适抢占的核心;
            if( ( taskTASK_IS_RUNNING( pxCurrentTCBs[ xCoreID ] ) != pdFALSE ) && ( xYieldPendings[ xCoreID ]