一、概念
实时操作系统,要求一个高的实时性,就不是像在一个死循环中放俩函数了。而是创建俩任务,也叫做俩进程,高速的轮流执行,提高实时性。
堆栈的申请是任务的基础。
二、创建任务
创建任务又两种方式,
第一种是动态创建任务,使用
BaseType_t xTaskCreate(TaskFunction_t pvTaskCode,
const char * const pcName,
const configSTACK_DEPTH_TYPE usStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask)
在动态创建任务的时候,指定好任务栈的大小后,系统使用函数xTaskCreate去为任务自动申请空间
第二种是静态创建
TaskHandle_t xTaskCreateStatic(TaskFunction_t pxTaskCode,
const char *const pcName,
const configSTACK_DEPTH_TYPE ulStackDepth,
void *const pvParameters,
UBaseType_t uxPriority,
StackType_t *const puxStackBuffer,
StaticTask_t *const pxTaskBuffer)
静态为任务申请空间,空间地址要手动申请,定义一个数组,传入数组头指针就算申请了空间
三、任务优先级
1、调度机制
优先级和时间片是两个概念,平级才有时间片。平级的时候用滴答时间时间片进行任务轮转,只有不同级别的时候才用任务优先级。
优先执行高优先级任务,要是高优先级的任务没有放弃任务权限,低优先级的任务得不到执行。
2、删除任务
使用下面的函数,参数是任务的句柄,TaskHandle_t xTask,删除哪个任务就把哪个人物的句柄放进去。
void vTaskDelete(TaskHandle_t xTask)//这是在任务一中删除任务2,需要加任务句柄,他杀所用
vTaskDelete(TNULL)//在自己的任务中删除自己,就可以用参数NULL,自杀所用
四、堆栈大小的管理
每个申请的任栈空间,都是由头和长度组成。任务栈空间分配的大小,要足够的支撑任务的运行,任务变量运行中和定义的变量大小不能超过,栈申请的空间大小。
那么如何确定一个任务需要多大的栈空间呢
五、任务管理
1、概念
多任务的交替执行如何实现,不是两个,是多个
任务切换的基础是,滴答tick中断,如果优先级相同的三个任务,只要滴答中断一到,就切换任务。就会产生时钟中断。
一到点,到到时间,就会发生Tick_IST中断,Tick基本上上是1ms,可以进行配置,配置成任何想要的时间。
2、任务状态
runing :运行状态:在跑着的任务
ready :准备状态:已经创建的任务,等待运行,可以随时运行,等待runing跑完,就要他们跑 了
blocked:阻塞状态:等待着去执行某个任务,可能某个任务的事情还没做完,又要执行了,比如说通信,一个信号还没发送完,又要发送了,应该就需要阻塞了。
suspended:暂停状态:主动休息了,被动休息了,被刮起了,挂起函数。
阻塞和暂停状态有啥不同呢:阻塞是等待某事情的发生,比如信号没法送完,自己任务中有个延时函数也起到了阻塞作用vTaskDelay(10);,这个任务里面有个中断需要触发,一直没有触发,就阻塞住了,一旦中断来了(延时够了),那这个阻塞的任务又可以进行了。
暂停状态是纯粹的休息,是主动和被动休息,需要运行的任务去重新唤醒这个阻塞的。
四种状态如何转换呢?
有三个任务被创建出来后,三个任务都会在read状态,当启用任务调度器的时候,主管任务调度,拉出来一个优先级高的任务去执行,runing状态,执行后然后执行下一个任务,这个read起来。
也可以挂起一个任务,用任务函数。
转换如下
内存上,不同的任务放在一个链表上,通过头指针找到任务的头。
创建任务,就是把这个任务放在一个,ready链表中
挂起任务,把这个任务放在了SUSpended列表中了
实验运行,讲解四种状态
几个重要函数:
xTaskGetTickCount()得到当前滴答的个数,发生了几次滴答
vTaskSuspend(xHandleTask3);挂起任务
vTaskResume(xHandleTask3);恢复任务
void Task2Function(void * param)
{
while (1)
{
task1flagrun = 0;
task2flagrun = 1;
task3flagrun = 0;
printf("2");
vTaskDelay(10); //延时阻塞的任务2
}
}
3、两个延时函数
VTaskDel ay和vTaskDelayUntil
vTaskDelayUntil这个延时函数有啥不一样的呢,就是用这个延时函数,保持每次任务执行的时间是一致的,不是说一会任务时间长,一会任务时间短。
vTaskDelayUntil相当于任劳任怨的延时函数,给弄得好好,把任务时间补齐,弄得规规整整的,城墙上的守卫者。
4、空闲任务和钩子函数
空闲任务,就是系统自带的优先级最低的任务函数,在这个优先级最低的任务函数中,要做的是一些清理工作。
可是两个任务互相执行,空闲任务得不到执行的话,内存会 爆炸掉吗?
vTaskDelete(NULL);杀死任务可以清理任务内存,可是能这个杀死只能是别人杀死,别人清理尸体,不能自己清理自己的实体。
面试重点:
我们可以在空闲任务中实现一些不重要的函数,不重要的东西,比如说清理尸体啥的,就需要钩子函数。就是不破化这个Freertos的源码,去改造空闲函数,用钩子钩住点东西。
使用钩子任务也有一些条件和约束:
钩子函数永远不能进入阻塞和暂停状态。
使用钩子函数方法:
前面个讲了如何创建任务,现在到了处理任务后事的时候了。
5、任务调度算法
优先级高的任务先执行,优先级低的任务交替执行
1、进入阻塞任务,的方式有好几种,等待任务的执行,任务进入阻塞的方式很重要,比如等待消息,消息阻塞有好几种方式
2、可以配置算法调度,看是否可以抢占,实际任务中要支持抢占
所谓的抢占,就是高优先级可以打断低优先级任务进行运行
如果抢占不了低优先级任务的话,那低优先级任务没有认出自己的任务的话,低优先级任务就要一直执行了。
3、同优先级的任务可以交替执行,也可以设置为先到先得
#define configUSE_PREEMPTION 1
#define configUSE_TIME_SLICING 0 //这个设置为0的话,低优先级的,在高优先级进行阻塞后也不进行轮转了,就只是先到先得了。
#define configIDLE_SHOULD_YIELD 1
4、空闲任务需要卑微的让出任务权限
#define configIDLE_SHOULD_YIELD 1
这个配置,配置空闲任务是否礼让别人,空闲任务运行的时候,可以让别人占有自己。
1是礼让,0是不礼让,都是0优先级的时候和其他任务 ,共同差不多的时候。
5、重点总结
调度策略
1、任务是否允许抢占
允许抢占的话,高优先级的任务先进性执行
不允许抢占的话,大家平等,哪个任务执行,就一直执行,除非自己让出执行权限
2、在允许抢占的时候还可以配置是否允许时间片轮转
允许的话,同优先级的任务交替执行
不允许的话,谁先运行谁就一直运行,除非主动让出 CPU和被抢占
3、在允许抢占和时间片轮转的时候,还要设置空闲任务是否会让步
如果让步的话,如果礼让,就空闲任务执行的时间少,用户任务优先执行
如果不礼让,那就会和用户同等级的0等级的的任务进行时间片轮转执行
六、同步、互斥和通信的概念
参考文章:
韦东山freeRTOS系列教程之【第一章】FreeRTOS概述与体验_freertos教程_韦东山的博客-CSDN博客
韦东山freeRTOS快速入门 (100ask.net)//视频教程