转载自 https://blog.csdn.net/zhoutaopower/article/details/107019521

任务管理是操作系统中重中之重,不管什么 OS ,任务的调度管理都是核心,FreeRTOS 也是一样;在深入到 FreeRTOS 任务管理的源码之前,鄙人觉得有必要先去从全局的角度进行把握,从全局到局部,从粗线条,到细节,鄙人觉得这样方可更快的熟悉相关的内部原理;

从全局来看的话,可以先梳理 FreeRTOS 关于任务相关的 APIs,支持的 Feature,以及相关的特性,这样一来,在深入到源码级分析的话,知道使用场景,便知道为何这样设计;

分析基于 FreeRTOS V 10.3.1

首先,FreeRTOS 任务支持如下特性:

1、多任务执行;

2、支持配置任务优先级;

3、支持任务的阻塞,挂起;

4、任务都是自己的栈空间;

5、支持周期性任务;

6、任务抢占;

7、协作式调度;

1、任务状态

在单核处理器上,多任务是宏观并行,微观串行的;每个任务可以处于不同的状态:

几乎所有的 OS 下,任务都分为了 Ready、Blocked、Running、Suspend 等状态;这样划分是根据具体的使用场景进行的;

Running:指的是正在运行的任务,在单核系统中,同一时刻只有一个任务处于 Running;

Ready:指的是可以被调度运行的任务,也就是处于就绪的任务;

Blocked:指的是因为某种原因(等待资源,等待时间)暂时不满足执行条件的任务的状态;

Suspend:指的是被挂起的任务,暂时不参与调度的状态;

2、任务创建

FreeRTOS 中,创建一个任务使用 xTaskCreate 接口:

BaseType_t xTaskCreate(  TaskFunction_t pvTaskCode,
const char * const pcName,
unsigned short usStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * pvCreatedTask);

参数的含义如下:

pvTaskCode:任务执行的函数,此函数必须是一个死循环,不会返回。

pcName:任务的名字,是一个字符串;最大长度由宏 configMAX_TASK_NAME_LEN 指定,该宏位于 FreeRTOSConfig.h 文件中。

usStackDepth:任务堆栈的大小,它的单位不是字节,比如在 32-bits 位宽的情况下,这个值设置为 100,那么就是分配了 400 字节大小的堆栈。

pvParameters:传递给任务执行函数的参数。

uxPriority:任务的优先级。

pvCreatedTask:创建任务成功后的任务句柄,后期可以使用这个句柄来调用任务相关的 API。

返回值的含义如下:

Return:如果创建任务成功,返回 pdPASS 否则返回 pdFAIL

比如:

创建了两个任务 vTask1 和 vTask2,堆栈深度为 1000,优先级都为 1,没有入参;

创建完后,两个任务都默认被添加进入了 Ready 状态,调用 vTaskStartScheduler() 开启调度器;

它们的实现都是无限循环,执行的时候,进行打印;

它们在时间线上的执行如图所示:

3、任务优先级

在任务初始化的时候,可以指定任务的优先级,同样在任务运行过程中,也可以通过 API 来改变任务的优先级;

FreeRTOS 支持的最大优先级由 configMAX_PRIORITIES 确定,优先级的值越低,代表优先级越低;0 是最低优先级,所有在 FreeRTOS 中,优先级的范围处于 0 ~ configMAX_PRIORITIES 之间;

因为任务存在优先级不一样,调度器总是选择处于 Ready 状态的优先级最高的任务;

比如:

Task 1 优先级是 1,Task 2 优先级是 2;两个 Task 都是之前的实现的情况下,每次调度器选择任务的时候,因为 Task 2 优先级高,每次都会选择 Task 2,那么 Task 1 就会被饿死:

4、任务阻塞

类似上面的情况,Task 1 被饿死,得不到执行,显然不是我们想要的,归根结底,是因为 Task 2 一直满足执行条件!按照设计来说,Task 2 优先级高,它应该是去做一些急需快速完成的任务,其他任何事情不准阻拦他,但是哪有任务一直都急需被完成呢?所以,正常情况下,Task 2 可能是在等待某个事件(比如,某个中断后)或者某个时间点来完成急需完成的任务,其他时间应该处于一种等待的状态,我们称之为阻塞,也就是 Blocked;

在 FreeRTOS 中,有两种方式可以让任务进入阻塞状态:

1、阻塞在时间上:也就是执行任务的时间未到;

2、等待事件:也就是对应的事件还没有发生;

如果类似上面的代码,在死循环里面调用了 vTaskDelay 函数,这就是会导致任务处于阻塞状态,等时间到了指定的延时后,才再次进入 Ready 状态,被调度器调度;

在这种设计下,不同优先级的任务同时运行的时候,就不会有被饿死的情况,同时当两个任务都没有执行的时候,时间让给 IDLE 线程:

5、任务挂起

被挂起的任务,进入 Suspend 状态,调度器在任务选择的时候,不再调度进入 Suspend 状态的任务,除非再次对此任务调用 Resume,重新进入 Ready 队列,接受调度器的调度;

6、空闲任务

在调度器初始化的时候,会创建一个 Idle 任务,这样可以确保至少有一个任务在运行;此任务优先级最低,为 0;

空闲任务用来在处理被删除的任务的内存,所有删除任务后,一定要确保空闲任务被运行,这样才能够内存回收;

FreeRTOS 支持空闲任务的钩子函数,当开启 configUSE_IDLE_HOOK 宏是 1 的时候,在空闲任务的时候,函数:

void vApplicationIdleHook( void );

被调用,常用的方式是在此实现低功耗相关的处理逻辑;

5、任务调度

5.1、抢占式调度

FreeRTOS 的任务调度基于周期性的 Tick 心跳,调度器从 Ready 状态列表中选择下一个优先级最高的任务投入运行;被阻塞的任务可以通过 event 来临或者阻塞时间到,重新进入 Ready 状态;

软件上,可以通过配置宏,来改变调度算法的行为,这些宏位于 FreeRTOSConfig.h

configUSE_PREEMPTION:配置为 1 则说明支持抢占式调度,否则称之为协作式调度;

注:协作式调度需要任务主动放弃 CPU,下一个才能够被调度;抢占式调度由系统决定调度;

configUSE_TIME_SLICING :配置为 1 的时候,同样优先级的任务会被轮转调度执行;否则优先级相同的任务,不会被轮转执行,只会执行其中一个;

最常用的配置是,上述两个都配置为 1;

configUSE_PREEMPTION = 1

configUSE_TIME_SLICING = 1

比如有 3 个任务:Task 1、Task 2、Task 3 如下所示,他们的优先级也标记出来;

Task 1 处于 blocked 状态,等待 event 满足条件;

Task 2 是周期性任务;

Task 3 是低优先级任务,也处于 blocked 状态,等待它的 event;

t1 时刻,Task 2 执行,Task 1 和 Task 3 的 event 都没满足;

t2 时刻,没有任务执行,Idle 线程执行;

t3 时刻,Task 3 的 event 满足了,被调度执行;

t4 时刻,执行 Idle;

t5 时刻,Task 3 的 event 满足了,被调度执行;

t6 时刻,Task 2 周期性任务来了,优先级高于 Task 3,即便是 Task 3 未执行完,OS 依然调度 Task 2 先执行;

t7 时刻,Task 2 执行完毕,OS 调度 Task 3;

t8 时刻,没有需要调度的任务,进入 Idle;

t9 时刻,Task 2 周期性任务来了,被调度;

t10时刻,最高优先级的 Task 1 的 event 满足,即便是 Task 2 未执行完,OS 依然调度 Task 1 执行;

t11 时刻,Task 1 执行完毕,继续调度尚未执行完毕的 Task 2;此刻低优先级的 Task 3 的 event 虽然也满足,但是优先级低,执行被推后;

t12 时刻,Task 2 执行完毕,Task 3 得以被调度;

t13 时刻,没有任务活动,调度 Idle 线程;

在 configUSE_PREEMPTION 和 configUSE_TIME_SLICING 都配置为 1 的时候,如果线程和 Idle 线程一样优先级,那么他们会被轮转调度:

上面的调度过程不在多说,Task 1 优先级最高,当满足他的 event 的时候,抢占其他任务执行,随后进入 blocked 状态;

在上面的例子中(有任务和 Idle 线程一样连续执行并且优先级一样),还有一个宏 configIDLE_SHOULD_YIELD,可能会导致调度器行为变化:

configIDLE_SHOULD_YIELD = 0,那么行为和上面的一样,也是默认情况;

configIDLE_SHOULD_YIELD = 1,那么 Idle 线程会 loop 一次然后主动让出 CPU,如下所示:

如果 :

configUSE_PREEMPTION = 1

configUSE_TIME_SLICING = 0

这表示调度器支持抢占,但是对于同样优先级的任务,不会去轮转,比如:

可以看到 Task 1 优先级高,可以抢占低优先级;

但是 Task 2 和 Idle 优先级一样,但是得不到轮转的调度;

5.2、协作式调度

当配置如下:

configUSE_PREEMPTION = 0

configUSE_TIME_SLICING = any value

代表调度器会进行协作式调度,什么意思呢?如下所示:

Task 1 优先级最高,Task 2 其次,Task 3 优先级最低;

t1 时刻,Task 3 处于运行,Task 1 和 Task 2 处于阻塞;

t2 时刻,Task 2 满足运行条件进入 Ready 状态,但是由于不支持抢占调度,所以无法执行,Task 3 继续执行;

t3 时刻,Task 1 满足运行条件进入 Ready 状态,但是由于不支持抢占调度,所以无法执行,Task 3 继续执行;

t4 时刻,Task 3 主动调用 taskYIELD() 函数,主动放弃 CPU,此刻调度器选择优先级最高的 Task 1 进行运行;

t5 时刻,Task 1 执行完毕,进入 blocked,调度器调度 Task 2 运行;

t6 时刻,Task 2执行完毕,进入 blocked,调度器调度 Task 3运行;

FreeRTOS --(7)任务管理之入门篇的更多相关文章

  1. Spring Cloud(一):入门篇

    Spring Cloud 简介 Spring Cloud 是一个基于 Spring Boot 实现的微服务架构开发工具,可以快速构建分布式系统中的某些常用模式,如配置管理.服务治理.断路器.智能路由. ...

  2. Membership三步曲之入门篇 - Membership基础示例

    Membership 三步曲之入门篇 - Membership基础示例 Membership三步曲之入门篇 -  Membership基础示例 Membership三步曲之进阶篇 -  深入剖析Pro ...

  3. spring boot(一):入门篇

    构建微服务:Spring boot 入门篇 什么是spring boot Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框 ...

  4. 1. web前端开发分享-css,js入门篇

    关注前端这么多年,没有大的成就,就入门期间积累了不少技巧与心得,跟大家分享一下,不一定都适合每个人,毕竟人与人的教育背景与成长环境心理活动都有差别,但就别人的心得再结合自己的特点,然后探索适合自己的学 ...

  5. 一个App完成入门篇(七)- 完成发现页面

    第七章是入门篇的倒数第二篇文章了,明天整个APP将进入收官. 本节教程主要要教会大家使用二维码扫描和用do_WebView组件加在html页面. 导入项目 do_WebView组件 扫描功能 自定义事 ...

  6. [原创]Linq to xml增删改查Linq 入门篇:分分钟带你遨游Linq to xml的世界

    本文原始作者博客 http://www.cnblogs.com/toutou Linq 入门篇(一):分分钟带你遨游linq to xml的世界 本文原创来自博客园 请叫我头头哥的博客, 请尊重版权, ...

  7. 转:OSGi 入门篇:模块层

    OSGi 入门篇:模块层 1 什么是模块化 模块层是OSGi框架中最基础的一部分,其中Java的模块化特性在这一层得到了很好的实现.但是这种实现与Java本身现有的一些模块化特性又有明显的不同. 本文 ...

  8. 转:OSGi 入门篇:生命周期层

    OSGi 入门篇:生命周期层 前言 生命周期层在OSGi框架中属于模块层上面的一层,它的运作是建立在模块层的功能之上的.生命周期层一个主要的功能就是让你能够从外部管理应用或者建立能够自我管理的应用(或 ...

  9. 【three.js详解之一】入门篇

    [three.js详解之一]入门篇   开场白 webGL可以让我们在canvas上实现3D效果.而three.js是一款webGL框架,由于其易用性被广泛应用.如果你要学习webGL,抛弃那些复杂的 ...

随机推荐

  1. JDK,JRE,JVM的作用及关系

    1.作用 JVM:Java虚拟机,保证Java语言跨平台 JRE:Java程序的运行环境 JDK:Java程序的开发环境 2.关系 JRE:JVM+类库 JDK:JRE+工具

  2. 高度不定,宽100%,内一div高不确定,如何实现垂直居中?

    verticle-align: middle; 绝对定位50%加translateY(-50%) 绝对定位,上下左右全0,margin:auto

  3. NO Oracle database,JUST USE Oracle client。远程导入导出dmp

    序言: 你会发现,exp.exe 和imp.exe均存在于Oracle数据库的安装bin目录下.而很多情况下,我们不想安装庞大的Oracle数据库,但想使用imp和exp等工具命令,在我们本地机对Or ...

  4. 记一次 Nuxt 3 在 Windows 下的打包问题

    0. 背景 之前用 Nuxt 3 写了公司的官网,包括了样式.字体图标.图片.视频等,其中样式和字体图标放在了 assets/styles 和 assets/fonts 目录下,而图片和视频则放在了 ...

  5. apollo规划控制视频-12basic motion planning and overview

  6. 媒体查询@media的使用

    媒体查询 参考:https://developer.mozilla.org...一个媒体查询由一个可选的媒体类型和零个或多个使用媒体功能的限制了样式表范围的表达式组成,例如宽度.高度和颜色.媒体查询, ...

  7. jQ模拟打字效果插件typetype

    typetype是一个jquery插件,可以模拟人类的打字效果. 效果图如下所示: 查看演示 http://weber.pub/demo/160828/jQuery.Type/jQuery.type. ...

  8. 小程序踩坑记录-上传图片及canvas裁剪图片后上传至服务器

    最近在写微信小程序的上传图片功能,趟过了一些坑记录一下. 想要满足的需求是,从手机端上传图片至服务器,为了避免图片过大影响传输效率,需要把图片裁剪至适当大小后再传输 主要思路是,通过wx.choose ...

  9. canvas离屏、旋转效果实践——旋转的雪花

    效果展示理论基础--"常见的canvas优化--模糊问题.旋转效果" 用离屏canvas画基础部分 1.封装画线函数 function drawLine(ctx,x1,y1,x2, ...

  10. 夏日葵电商:连锁零售店小程序o2o系统解决方案

    公众平台"附近小程序"功能上线后,一个主体账号可以同时绑定N+个门店,这对连锁零售店铺来说是重磅福利呀,无论你是通过搜索还是线下扫码进入小程序,线上与线下都完全贯通了,线上多种入口 ...