前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【RTOS训练营】任务调度(续)、任务礼让、调度总结、队列和晚课提问

【RTOS训练营】任务调度(续)、任务礼让、调度总结、队列和晚课提问

作者头像
韦东山
发布2022-09-16 09:34:16
5920
发布2022-09-16 09:34:16
举报
文章被收录于专栏:韦东山嵌入式韦东山嵌入式

一:任务调度(续)

上一节课讲调度,我们还没有讲完,

调度是理解后续所有知识的基础,可以说调度是RTOS中最重要的部分。

今天有一个同学提了这么一个问题,实时性怎么体现?

实时性,就体现在下面3点:

  1. 中断优先级,高于 任务优先级。“中断优先级最低”的中断,高于“任务优先级最高”的任务
  2. 中断里,高优先级的中断,会抢占低优先级的中断
  3. 任务里,高优先级的任务,会抢占低优先级的任务

对于中断先不管,

常用的调度策略是这样的:可抢占,时间片轮转,空闲任务礼让。

高优先级的任务一旦就绪,他马上就可以执行。

他一直独占CPU,一直执行,直到他休眠。

高优先级的任务,他只会跟同级的任务轮流执行,如果高优先级的任务只有他一个,他会一直独霸CPU。

如果高优先级的任务只有他一个,这时候就不存在什么时间片轮转。

就绪的任务,大家的优先级都相同,这时候才会使用时间片轮转。

那如果我们配置内核不使用时间片轮转:既然不轮流执行,那就是独占。

  1. 有高优先级的任务就绪了,他可以马上执行
  2. 如果大家的优先级都相同,只有当前任务主动放弃运行,别的任务才可以运行
  3. 假设任务一,任务二的优先级都是1,任务3的优先级是2。 任务3平时休眠,任务3一旦就绪,他马上就可以执行。 任务3再次放弃CPU的时候,就会导致一次调度

我们来看看一个图:

请添加图片描述
请添加图片描述

大家可以看到,在任务三休眠的那一瞬间,会触发调度。

第1个红色箭头:切换到任务1

第2个红色箭头,切换到任务2

第3个红色箭头,切换到任务1

第4个红色箭头,切换到任务2

第5个红色箭头,切换到空闲任务

大家可以看到,如果不轮流执行,只有两种情况:

  1. 贪婪的任务,可以一直霸占CPU
  2. 优先级高的任务,可以抢占CPU
  3. 当优先级高的任务休息了,底下那些贪婪的任务才会重新分配CPU

大家一定要注意,轮转,只发生在同级的任务之间。

二:任务礼让

接下来再讲讲礼让。

礼让,也只是发生在同级任务之间:空闲任务、其他优先级也是0的任务。

如果有任务的优先级大于0,他一旦就绪,根本就轮不到空闲任务执行:这时候谈何礼让?

在上一节课里我们布置了作业,作业1提到“task1、task2都执行了2次之后,为什么空闲任务推迟那么久才执行?”

这就是因为礼让。

再来分析一下上节课的作业,这个作业有助于大家理解调度。

请添加图片描述
请添加图片描述

我们创建了三个任务,启动调度器的时候又创建了空闲任务。

任务1、2,空闲任务,优先级都是0,他们被放在同一个链表。

  1. 任务3优先级最高,他先运行,然后主动调用vTaskDelay,放弃了CPU,这会触发一次调度
  2. 从优先级为0的那个链表里, 取出任务一来运行,任务一被放到队列的后面
请添加图片描述
请添加图片描述
  1. 一毫秒到了之后,从队列里取出第1个任务也就是任务2。他开始运行,并且也被放到了队列后面。
请添加图片描述
请添加图片描述
  1. 一毫秒到了之后,从队列里面取出第1个任务也就是空闲任务,他开始运行
请添加图片描述
请添加图片描述

大家可以看到,空闲任务实际上也是在运行的。

运行顺序是这样:task3, task1, task2, idle task

我们在逻辑分析仪里看不到,是因为还没有运行到钩子函数,空闲任务就主动放弃运行。

我们是在下面的钩子函数里,设置那个变量:

请添加图片描述
请添加图片描述

主动放弃之后,链表是这样的:

请添加图片描述
请添加图片描述

礼让之后,task1运行1ms、task2运行1ms

然后,空闲任务再次运行:从哪里开始运行?

请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

对于空闲任务,他是一个死循环,把这个死循环精简了一下:

请添加图片描述
请添加图片描述

从代码可以得知空闲任务做的所有事情:

  1. 清理工作很重要,礼让之前我先清理
  2. 什么情况下礼让?
请添加图片描述
请添加图片描述

pxReadyTasksLists[ tskIDLE_PRIORITY ] 就是: pxReadyTasksLists[ 0 ]

它里面存放的是:优先级为0的就绪任务。

如果这个队列的长度大于1,是不是意味着:除了空闲任务,还有其他优先级为0的就绪任务?

如果有其他优先级为0的就绪任务,我就礼让一下:发起一次调度,空闲任务躲到最后,让你们先运行。

请添加图片描述
请添加图片描述

当任务一、任务二,都执行了1ms,就能到空闲任务再次运行:

请添加图片描述
请添加图片描述

task1运行1次,

task2运行1次,

第1次礼让:task1运行1次,task2运行1次,idle任务运行1次, idle任务马上礼让

第2次task1运行1次,task2运行1次,idle任务继续运行

第一次礼让时不是空闲任务也执行了吗,为什么说没执行呢?

运行了,只是还没执行到钩子函数,我们在示波器上看不出来。

三:调度总结

我们再来总结一下调度:

1.默认了调度策略:可抢占、时间片轮转、空闲任务礼让

2.提几个问题:

假设 task1, task2优先级都是0,task3优先级是2

a. task3不休眠的话, task1, task2, idle任务都无法执行:对不对? 答案:对

b. task3不休眠的话, 中断也无法执行:对不对? 答案:不对

c. 高优先级的任务,应该尽快执行,然后让出CPU:对不对?答案:对

d. task1, task2从不休眠,空闲任务有没有机会执行?答案:有,轮转

e.task3从不休眠,空闲任务有没有机会执行? 答案:没有

  1. 高优先级任务会抢占低优先级的任务
  2. 同优先级的任务,轮流执行
  3. 空闲任务,跟其他同级的任务,也是轮流执行:只不过空闲任务执行的时间比较短,他会主动放弃CPU
  4. 中断,比所有的任务优先级都更高
请添加图片描述
请添加图片描述

理解这些队列,关键在于:

  1. 调度的时候,从 pxReadyTasksLists[31]pxReadyTasksLists[30]、……、pxReadyTasksLists[0],按照优先级从高往下查找这些队列
  2. 找出优先级最高的任务,来运行
  3. 最高优先级的任务,如果有多个的话,就轮流执行
  4. 有更高优先级的任务就绪任务时,低优先级的任务没有机会执行
  5. 任务调度的核心:当前任务执行完1tick,就乖乖的到后面去排队 调度时,取出队列的第1个任务,让他运行。

四:队列

多个任务之间传递信息,非常简单,用全局变量就可以。

如果你简单的使用全局变量来传递信息,会有一些缺点。

请添加图片描述
请添加图片描述

我们前面的程序,每个任务里面故意打印很短的字符串。

你们可以试验一下,你把这些字符串拉长,你会发现这些字符串会混杂在一起打印。

为什么呢?因为每个任务只能够运行一个tick,你打印很长的字符串的话,打印到中间的时候就被切换出去了,轮到别人打印了。

我们想写一个打印函数: 我打印之前,我会判断一下:如果有别的任务在使用串口,我就先不打印了,不去破坏别人。

来看看使用全局变量来怎么写代码:

请添加图片描述
请添加图片描述

这种方法行不行?我有一个全局变量,每个人都想去调用这个函数的话,都先判断一下。

大家一定要有一个概念,多任务: 假设有两个任务a和B,任务A执行的过程中,随时可能被任务B打断。

因此,可能出现这种情况:

请添加图片描述
请添加图片描述

task1执行到①的时候,它读入这个变量,发现是1。

在红线位置,被切换出去了,轮到task2运行,task2打印部分后,切换task1打印,就跳过了task1的判断,导致两个task打印交叉在一起。

再举一个例子:

请添加图片描述
请添加图片描述

我们可以直接上来就把这个变量减1,但是,减1的操作分为:读、减、写。

假设你刚读进来,就被切换出去了,

任务一和任务二,按照上面的黑色箭头运行,结果这两个任务还可以同时使用串口。

这种现象,是因为这两个任务都想去写同一个变量。

那如果这个变量,一个任务读,一个任务写,是不是就可以解决这种冲突的问题?

我们现在假设有两个任务,任务一做一个复杂的计算,任务2在等待他计算完成。

使用一个全局变量g_cal_ok来同步,任务1计算完之后,设置这个变量等于1,任务2循环检测这个变量,死等这个变量等于1。

请添加图片描述
请添加图片描述

上面的代码没有问题,可以正确运行,但是有什么缺点?

大家看到了,没有休眠-唤醒 机制。

使用全局变量,确实可以协调这些任务,但是没有休眠唤醒机制,task2一直在死循环等待。

如果我能够让任务2休眠,等任务一运行完毕,再让任务二重新运行,任务1就可以独占CPU,计算的更快。

任务之间可以有同步、互斥这样的操作,同步、互斥,怎么理解呢?

请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

同步、 互斥,相辅相成。

一句话理解同步与互斥:我等你用完卫生间,我再用卫生间。

什么叫同步?就是:哎哎哎,我正在用卫生间,你等会。

什么叫互斥?就是:哎哎哎,我正在用卫生间,你不能进来。

同步与互斥经常放在一起讲,是因为它们之的关系很大,而且“互斥”操作可以使用“同步”来实现。

我“等”你用完卫生间,我再用卫生间,这就是用“同步”来实现“互斥”。

分别举个应用的例子:

同步: Gui界面,在等待按键。这就是同步,同步的意思“等待”。

互斥:有两个程序都想去做全屏的屏幕显示,如果他们同时去使用屏幕,屏幕就是乱糟糟了。这个时候,我用屏幕时你不能够用,你用屏幕时我不能够用,这就是互斥。

同步强调先后,前后有依赖;互斥强调独占。

五:晚课学员提问

1. 问: 老师,有两个问题,FreeRTOS是实时系统体现在那个方面啊?

另外Task的调度抢占是可以发生在任意时刻吗?

比如正在执行与调度中断同优先级的其他中断,那么Tick中断来了也会去调度Task去执行吗??

答: 1. 中断优先级,高于 任务优先级。“中断优先级最低”的中断,高于“任务优先级最高”的任务。

2.中断里,高优先级的中断,会抢占低优先级的中断

3.任务里,高优先级的任务,会抢占低优先级的任务

2. 问: FreeRTOS有没有划分可抢占的区间,不可抢占的区间?

答: 当然有,比如:

  1. 关闭中断
  2. 执行代码
  3. 开启中断 在步骤2里,就是不可抢占的区间

3. 问: 那tick的中断优先级是最高的吗?

如果有比tick中断优先级更高的中断在运行是不是就要等高优先级中断执行完?

freertos有哪些情况下是要关闭中断再执行代码的?

答: 1.并不是tick中断优先级最高,它并不高,哪个中断优先级最高,由设计者决定。比如防火系统中,当然是烟雾报警的中断优先级最高。

2.高优先级的中断先运行,低优先级的中断当然要等

3.哪些情况下关闭中断?这也是由设计者决定,在中断A的处理函数里,它不想被高优先级的中断B抢占,A的函数就可以先关闭中断

4. 问: 老师我有一个问题 如果我有一个双核处理器,rtos是不是会自动同时运行两个同优先级的任务?

答: 你这样想问题:

  1. 双核处理器:看到的队列时一样的
  2. 如果队列里有3个任务:task3优先级最高,task1,2优先级相同
  3. 你觉得怎样运行更合理?

无论哪个CPU核,都是去队列里找到任务来运行

a.CPU1从队列里找到最高优先级的task3,运行

b.CPU2也不能闲着啊,它也去队列里找任务:task3? 不是,已经在运行了。所以它找到task1或task2,运行task1或task2

c.假设CPU2运行的是task1,1ms后从队列里挑出task2

5. 问: 常用的任务调度策略有:可抢占,时间片轮转,空闲任务礼让。

这是所有实时系统的常用策略吗?还是只是freertos?

答: 是常用策略,前面两个:可抢占\时间片轮转, 都是相同的。

空闲任务礼让:有些RTOS并没有这种说法

6. 问: 钩子函数是在空闲任务的时间段里周期的运行?

答: 1. 空闲任务:它里面有一个死循环,循环里面会调用钩子函数

  1. 但是执行的时间并不是周期的,空闲任务地位很低,执行时间没有保障了

7. 问: 礼让的条件是什么呀?

答: 我们配置了:可抢占、礼让,并且有其他“优先级为0的任务”就绪了。

8. 问: 那如果任务执行时间超过一个tick,且当前只有另外一个同级的任务,那么是会切换执行另一个同级任务的吧?

答: 一定会切换的。一个任务一般来说都是一个死循环,它期待的执行时间是“永远”。

9. 问: 老师。假设现在有task1 task2 task3三个任务。

task1为算法处理任务,task2 task3为传感器数据采集任务都是100hz,

task1要分别拿到task2和task3发过来的数据才去做算法的处理,

我现在做法是创建两个队列,用作task2 task3传输数据给task1,然后task1分别顺序等待两个队列都成功返回再往下处理算法。

这种方式可以吗?还有更好的方式吗?

答: 每次处理,都必须得到任务二、任务三的数据,顺序地、分别等待当然没问题

10. 问: 老师,普通任务会执行taskYIELD()函数主动放弃CPU吗?

答: 会的。

11. 问: 老师,如果是一个比较复杂的系统。有很多显示界面。那么所有的显示界面在一个任务里处理呢?还是各个任务分别调用GUI函数做显示?

答: 一般来说会有一个统一管理界面的任务。

12. 问: 老师,那Linux或安卓也也是显示有一个单独的任务来处理吗?只有显示任务里可以调用GUI函数?

答: 是的,有一个窗口管理的程序,其他任务都有自己的虚拟的显存,窗口管理的程序会根据他们的叠加关系组装出数据,再写LCD。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-07-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一:任务调度(续)
  • 二:任务礼让
  • 三:调度总结
  • 四:队列
  • 五:晚课学员提问
    • 1. 问: 老师,有两个问题,FreeRTOS是实时系统体现在那个方面啊?
      • 2. 问: FreeRTOS有没有划分可抢占的区间,不可抢占的区间?
        • 3. 问: 那tick的中断优先级是最高的吗?
          • 4. 问: 老师我有一个问题 如果我有一个双核处理器,rtos是不是会自动同时运行两个同优先级的任务?
            • 5. 问: 常用的任务调度策略有:可抢占,时间片轮转,空闲任务礼让。
              • 6. 问: 钩子函数是在空闲任务的时间段里周期的运行?
                • 7. 问: 礼让的条件是什么呀?
                  • 8. 问: 那如果任务执行时间超过一个tick,且当前只有另外一个同级的任务,那么是会切换执行另一个同级任务的吧?
                    • 9. 问: 老师。假设现在有task1 task2 task3三个任务。
                      • 10. 问: 老师,普通任务会执行taskYIELD()函数主动放弃CPU吗?
                        • 11. 问: 老师,如果是一个比较复杂的系统。有很多显示界面。那么所有的显示界面在一个任务里处理呢?还是各个任务分别调用GUI函数做显示?
                          • 12. 问: 老师,那Linux或安卓也也是显示有一个单独的任务来处理吗?只有显示任务里可以调用GUI函数?
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档