文章目录
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字节对齐 |
---|