一、FreeRTOS任务调度相关函数详解


1. FreeRTOS任务调度及相关函数分析

        操作系统的核心就是任务创建,删除,挂起,恢复和调度。要了解OS的调度,需要对CPU内核相关寄存器有一定的了解,本文会根据《Cortex-M4权威指南》进行针对性的讲解。

(后面还要继续分析MPU, SVC, PendSV,中断优先级问题,空闲函数)

1.1 任务创建过程分析

        任务创建可以分为静态创建和动态创建(其中会涉及到MPU内存保护单元,这里不做讲述,后面会根据《Cortex-M4权威指南》的MPU相关章节做详细说明),分别使用xTaskCreateStatic()和xTaskCreate()函数来创建Task。

1.1.1 任务创建函数分析

/* Tasks.c */
// 此段代码去掉了一些条件编译和Assert代码
#if(configSUPPORT_STATIC_ALLOCATION == 1) // 静态创建的任务无非就是要自己来申请栈和TCB的内存
TaskHandle_t xTaskCreateStatic(TaskFunction_t pxTaskCode,    // Task的函数
                                const char *const pcName,     // Task的名字 指针和指针指向的数据都是不可修改的
                                const uint32_t ulStackDepth,  // Task栈的深度
                                void* const pvParameters,     // Task参数指针,指针是不可修改的
                                UBaseType_t uxPriority,       // Task的优先级
                                StackType_t* const puxStackBuffer, // Task栈地指针,指针不可修改
                                StaticTask_t* const pxTaskBuffer)  // 在FreeRTOS.h中定义和TCB一样大小的结构体,是为了让用户层不接触到内核的结构体
{
    TCB_t* pxNewTCB;
    TaskHandle_t xReturn;
    
    if((puxStackBuffer != NULL) && (pxTaskBuffer != NULL)) // 确保Task栈和TCB指针都不能为NULL,在调用这个函数之前,必须已经定义pxTaskBuffer和puxStackBuffer,并申请了内存
    {
        pxNewTCB = (TCB_t*)pxTaskBuffer;
        pxNewTCB->pxStack = (StackType_t*)puxStackBuffer;
        
        #if(tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0) //如果定义了宏tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE == 1就证明了既可以动态创建Task也可以静态创建Task
        {
            pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATION_STACK_AND_TCB; // 如果是静态创建的Task就确保不要释放相关内存,删除任务的时候和动态申请的操作不同
        }
        #endif
        prvInitialiseNewTask(pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &xReturn, pxNewTCB, NULL); // 核心函数初始化新的任务
        prvAddNewTaskToReadyList(pxNewTCB);
    }
    else
    {
        xReturn = NULL;
    }
    return xReturn;
}
#endif

1.1.2 任务初始化函数分析

// 此函数没有打开MPU
static void prvInitialiseNewTask(TaskFunction_t pxTaskCode,    // Task的函数
                                 const char *const pcName,     // Task的名字 指针和指针指向的数据都是不可修改的
                                 const uint32_t ulStackDepth,  // Task栈的深度
                                 void* const pvParameters,     // Task参数指针,指针是不可修改的
                                 UBaseType_t uxPriority,       // Task的优先级
                                 TaskHandle_t* const pxCreatedTask, // 用于返回TaskHandle
                                 TCB_t* pxNewTCB)             // 用于返回TCB的指针
{
    StackType_t* pxTopOfStack; // 定义临时栈顶指针
    UBaseType_t x;
    
    #if( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1)
    {
        (void)memset(pxNewTCB->pxStack, (int)tskSTACK_FILL_BYTE, (size_t)ulStackDepth * sizeof(StackType_t)); // 往Task栈里面写入固定的值,0xa5U
    }
    #endif
    
    // 根据栈的生长方向确定栈顶的指针
    #if(portSTACK_GROWTH < 0) // Cortex-M系列都是-1,小于零 
    {
        pxTopOfStack = &(pxNewTCB->pxStack[ulStackDepth - (uint32_t)1]);
        pxTopOfStack = (StackType_t*)(((uint32_t*)(pxTopOfStack)) & (~(uint32_t)(0x7))); // 向下做8字节对齐 cortex-M系列
        #if(configRECORD_STACK_HIGH_ADDRESS == 1)
        {
            pxNewTCB->pxEndOfStack = pxTopOfStack; // 记录最高地址
        }
        #endif
    }
    #else 
    {
        pxTopOfStack = pxNewTCB->pxStack; // 栈顶指针就是栈的起始地址
        pxNewTCB->pxEndOfStack = &(pxNewTCB->pxStack[ulStackDepth - 1]);
    }
    #endif
    
    // 保存task的名字
    for(x = (UBaseType_t)0; x < (UBaseType_t)configMAX_TASK_NAME_LEN; x++)
    {
        pxNewTCB->pcTaskName[x] = pcName[x];
        
        if (pcName[x] == char(0x00)) // 如果名字的长度小于最大的长度,‘\0’后面就是0x00这个时候就break出循环
        {
            break;
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }
     pxNewTCB->pcTaskName[configMAX_TASK_NAME_LEN - 1] = '\0'; // 如果名字大于最大的长度 把最后一个字符强制设置成‘\0’,如果名字小于最大长度,就会有两个‘\0’,但是没有影响
     
     if (uxPriority >= (UBaseType_t)configMAX_PRIORITIES) // 如果大于等于最高优先级就要减1
     {
         uxPriority = (UBaseType_t)configMAX_PRIORITIES - (UBaseType_t)1;
     }
     else
     {
         mtCOVERAGE_TEST_MARKER();
     }
     
     pxNewTCB->uxPriority = uxPriority; // 把优先级计入TCB中
     
     #if (configUSE_MUTEXES == 1)       // 用于优先级继承
     {
         pxNewTCB->uxBasePriority = uxPriority;
         pxNewTCB->uxMutexesHeld = 0;
     }
     #endif
     
     vListInitialiseItem(&(pxNewTCB->xStateListItem)); // 初始化状态节点,主要用于调度(ready,blocked,suspended)
     vListInitialiseItem(&(pxNewTCB->xEventListItem)); // 初始化事件节点
     
     listSET_LIST_ITEM_OWNER(&(pxNewTCB->xStateListItem), pxNewTCB); // 状态节点的节点owner设置为自己的TCB
     
     // 事件列表的优先级值最大,优先级高,但是在事件列表的节点是优先级越小越放在前面,越早执行,因此此处xEventListItem的优先级应该是最大优先级减去本身的优先级
     listSET_LIST_ITEM_VALUE(&(pxNewTCB->xEventListItem), configMAX_PRIORITIES - uxPriority);
     listSET_LIST_ITEM_OWNER(&(pxNewTCB->xEventListItem), pxNewTCB); // 把xEventListItem的owner设成自己的TCB
     
     // 记录中断嵌套
     #if (configCRITICAL_NESTING_IN_TCB == 1)
     {
         pxNewTCB->uxCriticalNesting = (UBaseType_t)0U;
     }
     #endif
     
     // 记录应用TASK的tag
     #if (configUSE_APPLICATION_TASK_TAG == 1)
     {
         pxNewTCB->pxTaskTag = NULL;
     }
     #endif
     
     // 记录Task运行的时间
     #if (configGENERATE_RUN_TIME_STATS == 1)
     {
         pxNewTCB->ulRunTimeCounter == 0UL; 
     }
     #endif
     
     // 根据具体芯片初始化Task stack, 最后返回栈顶指针
     pxNewTCB->pxNewTCB = pxPortInitialiseStack(pxTopOfStack, pxTaskCode, pvParameters);
     
     if (pxCreateTask != NULL)
     {
         *pxCreatedTask = (TaskHandle_t)pxNewTCB; // pxCreateTask Task句柄是指向TCB指针的指针
     }
     else
     {
         mtCOVERAGE_TEST_MARKER();
     }
}

1.1.3 任务堆栈初始化分析

       下面是任务栈的结构图,对应与代码进行分析

栈顶,假设一开始栈顶就是8字节对齐
  • 5
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值