任务 状态
阻塞状态
如果一个任务正在等待某个事件,则称这个任务处于”阻塞态(blocked)”。阻塞态是非运行态的一个子状态。
任务可以在进入阻塞态以等待同步事件时指定一个等待超时时间,这样可以有效地实现阻塞状态下同时等待两种类型的事件。
xTicksToDelay 延迟多少个心跳周期。调用该延迟函数的任务将进入阻塞态,经延迟指定的心跳周期数后,再转移到就绪态。
举个例子,当某个任务调用 vTaskDelay( 100 )时,心跳计数值为 10,000,则该任务将保持在阻塞态,直到心跳计数计到10,100。常数 portTICK_RATE_MS 可以用来将以毫秒为单位的时间值转换为以心跳周期为单位的时间值。
挂起状态
“挂起(suspended)”也是非运行状态的子状态。处于挂起状态的任务对调度器而言是不可见的。让一个任务进入挂起状态的唯一办法就是调用 vTaskSuspend() API 函数;
而 把 一 个 挂 起 状 态 的 任 务 唤 醒 的 唯 一 途 径 就 是 调 用 vTaskResume() 或vTaskResumeFromISR() API 函数。大多数应用程序中都不会用到挂起状态。
就绪状态
如果任务处于非运行状态,但既没有阻塞也没有挂起,则这个任务处于就绪(ready,准备或就绪)状态。处于就绪态的任务能够被运行,但只是”准备(ready)”运行,而当前
尚未运行。
vTaskDelay() API 函数
void vTaskDelay( portTickType xTicksToDelay ) ;
xTicksToDelay 延迟多少个心跳周期。调用该延迟函数的任务将进入阻塞态,经延迟指定的心跳周期数后,再转移到就绪态。举个例子,当某个任务调用 vTaskDelay( 100 )时,心跳计数值为 10,000,则该任务将保持在阻塞态,直到心跳计数计到10,100。常数 portTICK_RATE_MS 可以用来将以毫秒为单位的时间值转换为以心跳周期为单位的时间值。
vTaskDelayUntil() API 函数
void vTaskDelayUntil( portTickType * pxPreviousWakeTime, portTickType xTimeIncrement );
pxPreviousWakeTime 此参数命名时假定 vTaskDelayUntil()用于实现某个任务以固定频率周期性执行。这种情况下 pxPreviousWakeTime保存了任务上一次离开阻塞态(被唤醒)的时刻。这个时刻被用作一个参考点来计算该任务下一次离开阻塞态的时刻。
pxPreviousWakeTime 指 向 的 变 量 值 会 在 API 函 数vTaskDelayUntil()调用过程中自动更新,应用程序除了该变量第一次初始化外,通常都不要修改它的值。
xTimeIncrement 此参数命名时同样是假定 vTaskDelayUntil()用于实现某个任 务 以 固 定 频 率 周 期 性 执 行 —— 这 个 频 率 就 是 由xTimeIncrement 指定的。
xTimeIncrement 的 单 位 是 心 跳 周 期 , 可 以 使 用 常 量portTICK_RATE_MS 将毫秒转换为心跳周期 。
总结:工程项目基本只使用vTaskDelay。功能简单实现方便。
任务 删除
vTaskDelete() API 函数
任务可以使用 API 函数 vTaskDelete()删除自己或其它任务。任务被删除后就不复存在,也不会再进入运行态。
需要说明一点,只有内核为任务分配的内存空间才会在任务被删除后自动回收。任务自己占用的内存或资源需要由应用程序自己显式地释放 .
总结:工程项目基本不会删除自己创建的任务,否则架构设计一般也不合理。
任务 调度
(一)优先级抢占式调度
这种类型的调度方案被称为”固定优先级抢占式调度”。所谓”固定优先级”是指每个任务都被赋予了一个优先级,这个优先级不能被内核本身改变(只能被任务修改)。 ”抢占
式”是指当任务进入就绪态或是优先级被改变时,如果处于运行态的任务优先级更低,则该任务总是抢占当前运行的任务
(二)选择任务优先级
单调速率调度(Rate Monotonic Scheduling, RMS)是一种常用的优先级分配技术。其根据任务周期性执行的速率来分配一个唯一的优先级。
(三)协作式调度
采用一个纯粹的协作式调度器,只可能在运行态任务进入阻塞态或是运行态任务显式调用 taskYIELD()时,才会进行上下文切换。
总结:工程项目基本使用抢占式调度。