一.main函数里面的栈是哪里分配的
main函数里面用到的栈,假设为msp,是汇编代码里面设定的,对于STM32F103,在汇编代码里的向量表设置了一个栈_initial_sp,那这个栈是给谁用的呢?
可以看到,这个_initial_sp在内存中分配了一个空间区域,Stack_Size=0x00000200,大小为16进制的200个字节,将这块内存空间的高地址放入_initial_sp,对于STM32F103,单片机启动的时候,会将_initial_sp的值存进一个main_sp 寄存器中。c函数中要用到栈,栈里面保存返回地址,保存局部变量,那么c函数什么时候用到栈呢?
在将_initial_sp这个值入栈后,接下来会跳转到Reset_Handler,Reset_Handler函数会调用__main函数,__main进行一系列初始化以后,会调用main函数。Reset_Handler下面有很多的中断函数,这些中断函数也是C函数,也是使用分配的这些栈的。
=========================================================================
二.任务执行流程
任务切换的流程在第九章已经了解了,现在假设任务Task 1,任务Task 2,任务Task 3的任务优先级都设置为0,那么这三个任务都会进入同一个就绪链表里面,进入pxReadyTasksList[0]就绪链表,那么这三个任务谁先执行?
//创建三个任务
int main( void )
{
prvSetupHardware();
xTaskCreate(vTask1, "Task 1", 1000, NULL, 1, NULL);
xTaskCreate(vTask2, "Task 2", 1000, NULL, 1, NULL);
xTaskCreate(vTask3, "Task 3", 1000, NULL, 1, NULL);
/* 启动调度器 */
vTaskStartScheduler();
/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
return 0;
}
可以看到,我们创建了三个优先级为01的任务,第一个任务Task1进入就绪链表以后,任务Task1就是当前任务,创建任务Task2的时候,因为任务Task2和任务Task1是同等任务优先级,所以任务Task2就成为了当前任务,同样的道理,任务Task3与任务Task2同等任务优先级,当任务Task3加入任务就绪链表以后,任务Task3就成为了当前任务。所以执行任务的话是任务Task3先运行,执行完后,把任务Task3放入链表后面;然后遵循链表先进先出的原则,依次执行任务Task1,任务Task2。
这是同等任务优先级的情况,记住,高优先级一定会抢占低优先级的任务,高优先级一但长时间处于运行状态(没有vTaskDelay( xDelay5ms )函数进行阻塞),低优先级根本得不到运行。如果没有更高优先级的任务,大家的优先级都一样,则轮流执行。
上面的main函数中,我们使用xTaskCreate函数创建了三个任务,那仅仅创建了三个任务吗?不是的,我们调用vTaskStartScheduler()函数启动任务调度器的时候,还创建了一个空闲任务,什么是空闲任务,有什么作用呢?
开启任务调度函数会创建一个空闲任务idle_Task,此时空闲任务会把CPU交给任务Task1,务Task1开始运行。空闲任务idle_Task执行其他任务的栈空间清理工作,例如任务Task1自己死掉了,那么任务Task1的栈空间里的内存空间由空闲任务idle_Task进行释放。
=========================================================================
三.任务调度器函数
启动任务调度器后会创建空闲任务,空闲任务idle_task的任务优先级也是0,会进入任务就绪链表pxReadyTasksList[0],当前任务就会指向空闲任务,如果创建3个任务优先级为0的任务Task 1,任务Task 2,任务Task 3,会先运行空闲任务。也就是说,空闲任务会影响调度过程。当前TCB指向idle_task,然后任务Task 1,任务Task 2,任务Task 3。
//任务调度函数
void vTaskStartScheduler( void )
{
BaseType_t xReturn;
/* Add the idle task at the lowest priority. */
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
{
StaticTask_t * pxIdleTaskTCBBuffer = NULL;
StackType_t * pxIdleTaskStackBuffer = NULL;
uint32_t ulIdleTaskStackSize;
/* The Idle task is created using user provided RAM - obtain the
* address of the RAM then create the idle task. */
vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize );
xIdleTaskHandle = xTaskCreateStatic( prvIdleTask,
configIDLE_TASK_NAME,
ulIdleTaskStackSize,
( void * ) NULL,
portPRIVILEGE_BIT, /
pxIdleTaskStackBuffer,
pxIdleTaskTCBBuffer );
if( xIdleTaskHandle != NULL )
{
xReturn = pdPASS;
}
else
{
xReturn = pdFAIL;
}
}
#else /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */
{
xReturn = xTaskCreate( prvIdleTask,
configIDLE_TASK_NAME,
configMINIMAL_STACK_SIZE,
( void * ) NULL,
portPRIVILEGE_BIT,
&xIdleTaskHandle );
}