FreeRTOS(四)任务管理

任务的基本概念

​ FreeRTOS 的任务可认为是一系列独立任务的集合。每个任务在自己的环境中运行。在任何时刻,只有一个任务得到运行,FreeRTOS 调度器决定运行哪个任务。调度器会不断的启动、停止每一个任务,宏观看上去所有的任务都在同时在执行。

​ 调度器做的事情就是,再任务切入切出的时候保存上下文环境信息(寄存器值、堆栈内容等),所以每一个任务都需要有自己的栈空间。当任务切出时,它的执行环境会被保存在该任务的栈空间中,这样当任务再次运行时,就能从堆栈中正确的恢复上次的运行环境,任务越多,需要的堆栈空间就越大,而一个系统能运行多少个任务,取决于系统的可用的 SRAM。

​ FreeRTOS 中的任务是抢占式调度机制,高优先级的任务可打断低优先级任务,低优先级任务必须在高优先级任务阻塞或结束后才能得到调度。同时FreeRTOS 也支持时间片轮转调度方式,只不过时间片的调度是不允许抢占任务的 CPU 使用权。

​ 一个任务通常都是运行在一个死循环中,如果不需要该任务了,可以调用API函数将其删除。

任务调度器的基本概念

​ FreeRTOS 中提供的任务调度器是基于优先级的全抢占式调度:在系统中除了中断处理函数、调度器上锁部分的代码和禁止中断的代码是不可抢占的之外,系统的其他部分都是可以抢占的。

​ 一般强制限定最大可用优先级数目为 32。

​ 在系统中,当有比当前任务优先级更高的任务就绪时,当前任务将立刻被切出,高优先级任务抢占处理器运行。但是查找最高优先级任务的过程决定了调度时间是否具有确定性。如果存在n个任务,那么寻找最高优先级任务的时间就和n相关,此时如何确定调度时间或者说确定下一级优先级任务的时间就会影响系统的实时性。这样就要找到方法来确定寻找最高优先级任务的时间。

​ FreeRTOS 内核中采用两种方法寻找最高优先级的任务:

  1. 第一种方法:通用的方法,在就绪链表中查找从高优先级往低查找 uxTopPriority,因为在创建任务的时候已经将优先级进行排序,查找到的第一个 uxTopPriority 就是我们需要的任务,然后通过 uxTopPriority 获取对应的任务控制块。这样就保证调度时间确定在了一个较小的范围内。

  2. 第二种方法:则是特殊方法,利用计算前导零指令 CLZ,直接在uxTopReadyPriority 这个 32 位的变量中直接得出uxTopPriority,这样子就知道哪一个优先级任务能够运行,这种调度算法比普通方法更快捷,但受限于平台(在 STM32 中我们就使用这种方法)

任务状态迁移

FreeRTOS 系统中的每一个任务都有多种运行状态:就绪态、运行态、挂起态、阻塞态。

(1):创建任务→就绪态(Ready):

​ 任务创建完成后进入就绪态,表明任务已准备就绪,随时可以运行,只等待调度器进行调度。

(2):就绪态→运行态(Running):

​ 发生任务切换时,就绪列表中最高优先级的任务被执行,从而进入运行态。

(3):运行态→就绪态:

​ 有更高优先级任务创建或者恢复后,会发生任务调度,此刻就绪列表中最高优先级任务变为运行态,那么原先运行的任务由运行态变为就绪态,依然在就绪列表中,等待最高优先级的任务运行完毕继续运行原来的任务(此处可以看做是 CPU 使用权被更高优先级的任务抢占了)。

(4):运行态→阻塞态(Blocked):

​ 正在运行的任务发生阻塞(挂起、延时、读信号量等待)时,该任务会从就绪列表中删除,任务状态由运行态变成阻塞态,然后发生任务切换,运行就绪列表中当前最高优先级任务。

(5):阻塞态→就绪态:

​ 阻塞的任务被恢复后(任务恢复、延时时间超时、读信号量超时或读到信号量等),此时被恢复的任务会被加入就绪列表,从而由阻塞态变成就绪态;如果此时被恢复任务的优先级高于正在运行任务的优先级,则会发生任务切换,将该任务将再次转换任务状态,由就绪态变成运行态。

(6) (7) (8):就绪态、阻塞态、运行态→挂起态(Suspended):

​ 任务可以通过调用 vTaskSuspend() API 函数都可以将处于任何状态的任务挂起,被挂起的任务得不到CPU 的使用权,也不会参与调度,除非它从挂起态中解除。

(9):挂起态→就绪态:

​ 把一个挂起状态的任务恢复的唯一途径就是调用vTaskResume() 或 vTaskResumeFromISR() 函数,如果此时被恢复任务的优先级高于正在运行任务的优先级,则会发生任务切换,将该任务将再次转换任务状态,由就绪态变成运行态。

在这里插入图片描述

任务状态的概念

任务状态通常分为以下四种:

就绪(Ready):

​ 该任务在就绪列表中,就绪的任务已经具备执行的能力,只等待调度器进行调度,新创建的任务会初始化为就绪态。

运行(Running):

​ 该状态表明任务正在执行,此时它占用处理器,FreeRTOS 调度器选择运行的永远是处于最高优先级的就绪态任务,当任务被运行的一刻,它的任务状态就变成了运行态。

阻塞(Blocked):

​ 如果任务当前正在等待某个时序或外部中断,我们就说这个任 务处于阻塞状态,该任务不在就绪列表中。包含任务被挂起、任务被延时、任务正在等待信号量、读写队列或者等待读写事件等。

挂起态(Suspended):

​ 处于挂起态的任务对调度器而言是不可见的,
​ ①让一个任务进入挂起状态的唯一办法就是调用 vTaskSuspend()函数;
​ ②把一个挂起状态的任务恢复的唯一途径就是调用vTaskResume()或vTaskResumeFromISR()函数;
​ 我们可以这么理解挂起态与阻塞态的区别,当任务有较长的时间不允许运行的时候,我们可以挂起任务,这样子调度器就不会管这个任务的任何信息,直到我们调用恢复任务的API函数;而任务处于阻塞态的时候,系统还需要判断阻塞态的任务是否超时,是否可以解除阻塞。

常用任务函数

任务挂起函数

  1. vTaskSuspend() 挂起指定任务。被挂起的任务绝不会得到 CPU 的使用权,不管该任务具有什么优先级。

  2. vTaskSuspendAll() 将所有的任务都挂起,即将调度器锁定,并且这个函数是可以进行嵌套的,说白了挂起所有任务就是挂起任务调度器。

任务恢复函数

  1. vTaskResume() 任务恢复就是让挂起的任务重新进入就绪状态,恢复的任务会保留挂起前的状态信息,在恢复的时候根据挂起时的状态继续运行。如果被恢复的任务在所有就绪态任务中,处于最高优先级列表的第一位,那么系统将进行任务上下文的切换。
  2. xTaskResumeFromISR() 专门用在中断服务程序中。 无论通过调用一次或多次vTaskSuspend()函数而被挂起的任务,只需调用一次 xTaskResumeFromISR()函数即可解挂。要想使用该函数必须在 FreeRTOSConfig.h 中把 INCLUDE_vTaskSuspend 和INCLUDE_vTaskResumeFromISR 都定义为 1 才有效。
  3. xTaskResumeAll() 解挂所有任务,即恢复调度器。

任务删除函数

​ vTaskDelete()vTaskDelete()用于删除一个任务。当一个任务删除另外一个任务时,形参为要删除任务创建时返回的任务句柄,如果是删除自身,则形参为NULL。

​ 要想使用该函数必须在FreeRTOSConfig.h中把 INCLUDE_vTaskDelete 定义为 1,删除的任务将从所有就绪,阻塞,挂起和事件列表中删除。

任务延时函数

​ vTaskDelay() 用于阻塞延时,调用该函数后,任务将进入阻塞状态,进入阻塞态的任务将让出 CPU 资源。延时的时长由形参xTicksToDelay决定,单位为系统节拍周期, eg:系统的时钟节拍周期为1ms,调用vTaskDelay(1)的延时时间则为1ms。

​ vTaskDelay() 延时是相对性的延时,它指定的延时时间是从调用 vTaskDelay()结束后开始计算的,经过指定的时间后延时结束。比如 vTaskDelay(100), 从调用 vTaskDelay()结束后,任务进入阻塞状态,经过 100 个系统时钟节拍周期后,任务解除阻塞。

​ vTaskDelayUntil() 是绝对延时函数,这个绝对延时常用于较精确的周期运行任务,比如我有一个任务,希望它以固定频率定期执行,而不受外部的影响,任务从上一次运行开始到下一次运行开始的时间间隔是绝对的。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值