浅析FreeRTOS源码:prvAddNewTaskToReadyList函数

1、函数作用

一看名字就知道是把刚创建好的New Task添加到就绪列表里。这个函数在task.c这个文件中。

2、函数分析

先看函数签名:

static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )

传入的参数是新建函数的TCB,在浅析xTaskCreate这篇文章中可以知道,由prvInitialiseNewTask函数初始化了一个任务的TCB,随后这个TCB也就被传入了我们现在分析的这个函数。
函数里做的第一件事是调用了这么一个函数:

taskENTER_CRITICAL();

看名字是“进入临界区”。官方解释是:“确保正在更任务列表的时候,中断不介入”。那么我猜测,这个函数是用来关闭其他中断的。这个函数拓展到vPortEnterCritical(),我们看这个函数的实现:

void vPortEnterCritical( void )
{
    portDISABLE_INTERRUPTS();   // 失能中断
    uxCriticalNesting++;        // 嵌套数+1

    /* This is not the interrupt safe version of the enter critical function so
     * assert() if it is being called from an interrupt context.  Only API
     * functions that end in "FromISR" can be used in an interrupt.  Only assert if
     * the critical nesting count is 1 to protect against recursive calls if the
     * assert function also uses a critical section. */
    if( uxCriticalNesting == 1 )
    {
        configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
    }
}

果然,进函数第一件事就是失能中断。同时官方注释提醒我们,这个进入临界区的函数并非中断安全的,中断里调用的,应该是有FromISR后缀的接口函数。当嵌套数为1时,做声明的动作,官方解释是避免重复调用。
继续分析:有进入临界区的操作,就必然有退出临界区的操作。在退出临界区之前,有一大段源码要分析:

{
    uxCurrentNumberOfTasks++;
    if( pxCurrentTCB == NULL )
    {
        /* There are no other tasks, or all the other tasks are in
         * the suspended state - make this the current task. */
        pxCurrentTCB = pxNewTCB;

        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 )
            {
                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 );
}

首先是uxCurrentNumberOfTasks这个变量,它是task.c这个文件里的全局变量,记录有当前有多少个任务,初始化为0。
pxCurrentTCB也是当前文件的全局变量,是TCB指针,初始化为NULL。这个变量为NULL,说明没有其他任务,或者所有其他任务都被挂起了,则该指针指向函数传入的参数TCB。
如果当前任务数为1,执行新建任务列表的函数prvInitialiseTaskLists
如果pxCurrentTCB已经指向别的任务了,且如果此时调度器未在运行,把这个指针指向任务和当前任务的优先级进行对比,如果当前任务优先级高,则pxCurrentTCB指向当前任务。
又来了个变量uxTaskNumber,这个变量可能是与调试有关。下面的宏判断,若开启了追踪设备,将uxTaskNumber保存至TCB中。
traceTASK_CREATE( pxNewTCB )是宏定义,没有实现。
prvAddTaskToReadyList( pxNewTCB )这一步才是真正的把任务添加到就绪列表的操作,对比当前函数prvAddNewTaskToReadyList可以得知,这个函数主要是针对新创建的任务做了处理。
我们索性看一下prvAddTaskToReadyList( pxNewTCB )这个宏都做了些什么:

#define prvAddTaskToReadyList( pxTCB )                                                                 \
    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 )

第一个traceMOVED_TASK_TO_READY_STATE没实现。
第二个taskRECORD_READY_PRIORITY记录任务的优先级,记录在uxTopReadyPriority这个变量里。
第三个listINSERT_END的主要作用是将状态列表Item插入到任务列表里,其底层是操作链表。
第四个tracePOST_MOVED_TASK_TO_READY_STATE没实现。

回到函数主体。portSETUP_TCB( pxNewTCB );拓展到(void)(pxNewTCB)仅仅是点了一下这个变量,估计是避免编译器报错变量定义了但是未使用吧。
至此,临界区里的工作就做完了,接下来是退出临界区taskEXIT_CRITICAL(),它拓展到如下函数:

void vPortExitCritical( void )
{
    configASSERT( uxCriticalNesting );
    uxCriticalNesting--;

    if( uxCriticalNesting == 0 )
    {
        portENABLE_INTERRUPTS();
    }
}

可以看出其操作与进入临界区的操作是相反的,这里就不过多解释了。
最后一部分:

if( xSchedulerRunning != pdFALSE )
{
    /* If the created task is of a higher priority than the current task
     * then it should run now. */
    if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority )
    {
        taskYIELD_IF_USING_PREEMPTION();
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }
}
else
{
    mtCOVERAGE_TEST_MARKER();
}

如果调度器正在运行(吐槽一下,为什么不写成==pdTRUE呢,非要写成!=pdFALSE),而且当前任务优先级比新建任务优先级低,则让当前任务主动让出CPU使用权。

3、总结

这个函数做了啥:
1、在临界区中调用prvInitialiseTaskLists初始化任务列表 / 把“当前TCB”指向优先级大的TCB。
2、记录任务数量。
3、通过修改链表的方式,把任务添加进任务列表。
4、让调度器运行优先级较高的任务。

  • 25
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值