推荐使用vscode+source insight阅读代码,vscode阅读代码未使用的宏定义会变暗,source insight可以非常方便的查看函数间的调用关系。接下来开始我们的学习之旅.。。。。。。
什么是任务?
我们常认为,C语言程序的核心是函数和变量。我们常说创建一个变量、创建一个函数,就是创建一个任务了吗?并不是。变量只是一个名字,我们无法单纯依靠变量去做任何事;函数是看到名字就能够知道是有什么作用、有什么功能的东西,它保存在flash上,在flash上的函数无需再次保存。函数和变量都是静态的,而任务应该是运行起来的函数,因为任务不仅仅包含函数和变量,还包含着它们运行所需要的环境。当任务在运行的时候,现在暂停了,但是未退出,我们为了让他下次重新运行的时候需要做些什么?
答案当然是保存数据啦,那我们怎么保存它、保存什么?怎么恢复它、恢复什么?这就涉及到C语言的本质了,我们不在这里展开讲,后续会单开一篇文章。
任务的创建
在freertos中,我们使用xTaskCreate函数来创建任务,我们进入查看它的源码是这样的
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,要运行的函数
const char * const pcName, 给它安排的名字
const configSTACK_DEPTH_TYPE usStackDepth,栈大小
void * const pvParameters,参数
UBaseType_t uxPriority,优先级
TaskHandle_t * const pxCreatedTask TCB结构体 )
名字就是任务的名字。
栈大小在freertos中是这样分配的
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
那么栈大小我们怎么知道要分配多大呢?这也是C语言的本质,我们会在后续单开一篇讲。
参数不重要,一般传NULL。
优先级很重要,任务运行就是高优先级运行,优先级数据越高优先级越大 在freertos源码是这样的
#define configMAX_PRIORITIES ( 5 ) freertos 默认定义了优先级最大是5。
TCB结构体这是传出参数,进入源码我们看到它有这些东西
ListItem_t xStateListItem;
ListItem_t xEventListItem;
上面两个链表先不管,后续会讲到
UBaseType_t uxPriority; 创建时传入的优先级
StackType_t * pxStack; 创建时传入的栈大小
char pcTaskName[ configMAX_TASK_NAME_LEN ]; 创建时传入的名字
任务的状态与优先级
任务的状态
在freertos中,任务一共有4种状态:
就绪状态:做好了一切准备工作,只等分配到CPU时间片就能运作的状态
运行状态:正在执行任务的状态
阻塞状态:因外部事件无法执行,需要等某个事件发生了才能执行,且有超时时间,时间到了就会退出阻塞态,但过了超时时间事件还没发生还得重新回到阻塞态,这会浪费CPU资源
挂起状态:没有超时时间,当有一个任务需要运行一断时间,过一段时间后再回复执行时就可以将这个任务放入挂起状态
任务的优先级
在task.c文件中, 我们看到有 pxReadyTasksLists[ configMAX_PRIORITIES ] pxDelayedTaskList xPendingReadyList三类链表。任务的调度就是靠这三类执行的,
我们实操一下,首先先写了俩个任务
xTaskCreate( vTask1, "Task 1", 1000, NULL, 1, NULL );
xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, NULL );
因为configMAX_PRIORITIES 默认是5,所以有pxReadyTasksLists[0] ~~pxReadyTasksLists[4],总共5个链表。
在源码中,通过 prvAddTaskToReadyList( pxNewTCB );传入就绪链表
放入的位置是链表的最后,在同优先级情况下是先来先到原则,最新创建的任务先运行,就是人类世界的排队,源码是这样呈现的
vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) );
如果创建的任务的优先级高于当前任务,那么它应该立即运行。
if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority )
{
taskYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
在优先级为0下,还有一个空闲任务,空闲任务永远优先级最低,永远先礼让其他任务。
if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( UBaseType_t ) 1 )
{
taskYIELD();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
那空闲任务存在的意义是什么?如垃圾回收处理。
在其他高级语言中如JAVA有JVM,JVM有自带的垃圾回收机制,不需要程序去执行,但在C语言中却没有,所以有个空闲任务可以去执行一些清理任务
任务的调度
在freertos中谁来调度任务呢?
答案是tick中断。在freertos中是1m执行一次。tick可以挑出优先级高的任务先执行,也会判断是否要去切换任务。