前面文章 < FreeRTOS 任务调度 任务创建 > 介绍了 FreeRTOS 中如何创建任务以及其具体实现。
一般来说, 我们会在程序开始先创建若干个任务, 而此时任务调度器还没又开始运行,因此每一次任务创建后都会依据其优先级插入到就绪链表,同时保证全局变量 pxCurrentTCB 指向当前创建的所有任务中优先级最高的一个,但是任务还没开始运行。
当初始化完毕后,调用函数 vTaskStartScheduler启动任务调度器开始开始调度,此时,pxCurrentTCB所指的任务才开始运行。
所以, 本章,介绍任务调度器启动以及如何进行任务切换。
调度器涉及平台底层硬件操作,本文以Cotex-M3 架构为例, 具体可以参考 《Cortex-M3权威指南》(文末附)
分析的源码版本是 v9.0.0
(为了方便查看,github 上保留了一份源码Source目录下的拷贝)
创建任务后,系统不会自动启动任务调度器,需要用户调用函数 启动调度器。 该函数被调用后,会先创建系统自己需要用到的任务,比如空闲任务 prvIdleTask,定时器管理的任务等。 之后, 调用移植层提供的函数 xPortStartScheduler 。
代码解析如下,
void vTaskStartScheduler( void )
{
BaseType_t xReturn;
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
{
// 采用静态内存创建空闲任务
StaticTask_t *pxIdleTaskTCBBuffer = NULL;
StackType_t *pxIdleTaskStackBuffer = NULL;
uint32_t ulIdleTaskStackSize;
// 获取静态内存地址/参数
vApplicationGetIdleTaskMemory(
&pxIdleTaskTCBBuffer,
&pxIdleTaskStackBuffer,
&ulIdleTaskStackSize );
// 创建任务
// 空闲任务优先级为 0, 也就是其优先级最低
// !! 但是, 设置了特权位, 所以其运行在 特权模式
xIdleTaskHandle = xTaskCreateStatic(prvIdleTask, "IDLE",
ulIdleTaskStackSize, (void *) NULL,
(tskIDLE_PRIORITY | portPRIVILEGE_BIT),
pxIdleTaskStackBuffer,
pxIdleTaskTCBBuffer);
if( xIdleTaskHandle != NULL )
{
xReturn = pdPASS;
}
else
{
xReturn = pdFAIL;
}
}
#else
{
// 动态申请内存创建任务
xReturn = xTaskCreate(prvIdleTask,
"IDLE", configMINIMAL_STACK_SIZE,
(void *)NULL,
(tskIDLE_PRIORITY | portPRIVILEGE_BIT),
&xIdleTaskHandle );
}
#endif
// 如果工程使用了软件定时器, 需要创建定时器任务进行管理
#if ( configUSE_TIMERS == 1 )
{
if( xReturn == pdPASS )
{
xReturn = xTimerCreateTimerTask();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif
if( xReturn == pdPASS )
{
// 关闭中断, 避免调度器运行前节拍定时器产生中断
// 中断在第一个任务启动时恢复
portDISABLE_INTERRUPTS();
#if ( configUSE_NEWLIB_REENTRANT == 1 )
{
// 如果使用了这个库
// 更新第一个任务的的指针到全局变量
_impure_ptr = &( pxCurrentTCB->xNewLib_reent );
}
#endif
// 初始化变量
xNextTaskUnblockTime = portMAX_DELAY;
xSchedulerRunning = pdTRUE;
xTickCount = ( TickType_t ) 0U;
// 如果启动统计任务运行时间, 宏 configGENERATE_RUN_TIME_STATS = 1
// 需要定义以下宏, 初始化一个定时器用于该功能
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();
// 设置系统节拍计数器, 启动任务
// 硬件相关, 由系统移植层提供, 下面介绍
if( xPortStartScheduler() != pdFALSE )
{
// 不会运行到这里, 如果调度器运行正常
}
else
{
// 当调用 xTaskEndScheduler()才会来到这里
}
}
else
{
// 内存不足,创建空闲任务/定时任务失败, 调度器启动失败
configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );
}
// 预防编译器警告
( void ) xIdleTaskHandle;
}
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-33581-1.html
至于被平均的部份肯定在不你手中
统一是大势所趋