文章目录
边学边记,朋友们多多指教。
补充
补充一些东西。
任务进出临界区
临界区(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 ]