前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【RTOS训练营】作业讲解、队列和环形缓冲区、队列——传输数据、队列——同步任务和晚课提问

【RTOS训练营】作业讲解、队列和环形缓冲区、队列——传输数据、队列——同步任务和晚课提问

作者头像
韦东山
发布2022-09-16 09:35:29
3810
发布2022-09-16 09:35:29
举报

一:作业讲解

上一节课留了一个课后作业:

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

为什么IDLE任务的波形有大有小?

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

task2运行了1个tick,轮到idle任务。

idletask->hook->flagIdleTaskrun=1, 礼让,轮到task1运行,

task1从printf中间继续运行,打印完下一个字符后,才设置flagIdleTaskrun=0,

可以看到,flagIdleTaskrun等于1的时间:在idle任务里,也在task1里,

所以这个变量用来表示任务的运行时间:并不准确。

改成这样就没问题:

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

现在可以看到了,空闲任务运行的时间非常非常短:

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

二:队列和环形缓冲区

队列没什么复杂的,本质就是一个环形缓冲区,再加上任务的休眠和唤醒

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

对于环形缓冲区,它有如下要点:

  1. 有读写位置
  2. 写到buff的尾部之后,要绕到头部
  3. 读到buff的尾部之后,要绕到头部

这里再复习下环形缓冲区。

我们假设有一个数组,一开始里面没有数据,r = w = 0。

我要往里面写数据的时候,怎么写?

buf[w] = val;
w = (w+1);
if (w == 4)
	w = 0;

刚写的位置等于4的时候,就把它复位为0。

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

那怎么读数据呢?

val = buf[r];
r = r + 1;
if (r == 4)
	r = 0;

我们先不管空或者满,读写数据就是那么简单。

以面向对象的思想,我们就会抽象出一个结构体:

struct queue {
 char buf[4];
 int r;
 int w;
};

队列也是这么做的,我们来看看队列结构体长什么样:

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

图里面“环形缓冲区”那几个文字一个指向的是“写位置”,另一个指向的是“读位置”。

以前我们使用下标来表示读写的位置:

struct queue {
 char buf[4];
 int r;
 int w;
};

现在别人直接使用指针:

int8_t * pcWriteTo;  // 写位置
int8_t * pcReadFrom; // 读位置

这两个指针指向缓冲区在某些位置,这个缓冲区什么时候创建?

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

这个函数就会去分配一块内存,内存的长度就是: 1*sizeof(int)

我们这个例子里面,在这个队列里他只分配了一个元素的空间,你当然可以让他分配多个元素。

在以前讲环形缓冲区的时候,有同学问过一个问题:能不能够创建一个通用的环形缓冲区?可以用来传递任意大小的数据。

可以,队列就是:你可以指定这个环形缓冲区里每一个元素多大,有多少个元素。

可以看到,它创建一个队列的时候,会去创建一个结构体Queue_t,还会去分配保存数据的空间:

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

三:队列——传输数据

我们可以通过队列传递数据、同步任务,实现互斥操作。

怎么传递数据?

一个任务写对队列,另外一个任务读队列。

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

有同学对头和尾很容易混淆,在上面那个队列里我写了两个数据,红色斜线表示有数据。

第1个数据放在头部那里,第2个数据在尾部旁边。

要读数据的时候,先读第1个数据,就是从头部读。

假设读到了一个数据,头、尾就是这样的:

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

这时候如果我再写一个数据,头和尾就是这样的:

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

对于队列操作,我们一般来说是往尾部上写数据,

但是你也可以说:我这个数据比较着急处理,我可以写到头部去。

但读数据的时候,永远是从头部读。

四:队列——同步任务

我们可以通过队列来传递数据,那么怎么通过队列来同步任务呢?

我们以前写了一个项目:

业务系统在等待按键或者网络数据,我们可以把业务子系统写成一个任务。

读取按键,我我们也可以单独写成一个任务,

读取网络数据,也可以单独写成一个任务。

业务系统:他可以去读队列,没有数据的时候他就休眠。

谁把他唤醒?谁构造了数据、谁写了队列,就有这个任务去唤醒。

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

对于这几条语句,大家慢慢细品,我会提出很多问题:

task2读队列,因为没有数据正在休眠, task1写队列, task2优先级比task1高 task2会立刻被唤醒、立刻执行

任务2在等待数据,任务1写数据后,他怎么知道要去唤醒任务2?他怎么知道任务2在等待数据?

看看队列的结构体,里面有2个链表:

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

一个是用来管理那些等待空闲以便写入数据的任务,

另一个用来管理那些等待数据以便读出数据的任务。

task2读队列,因为没有数据而休眠,并且会把自己放在队列的这个链表上:xTasksWaitingToReceive

task1写队列,会去队列的这个链表:xTasksWaitingToReceive,挑出一个任务把它唤醒。

任务的切换,就是把任务放在不同的链表,再来分析task2读队列时:

1.因为没有数据而休眠 ==> 从ready list放到delay list

放入delay list是因为,可能它并不想死等,还给自己规定了超时时间,

时间到了,tick中断要从delay list把它唤醒。

2.并且会把自己放在队列的这个链表上xTasksWaitingToReceive

一个任务,想去读队列,但是队列里没有数据,就休眠:会把自己放入两个链表

我们再来看看任务1,任务2读不到数据就休息了,任务1写数据后发生了什么事?

task1写队列,会去队列的这个链表:xTasksWaitingToReceive,挑出一个任务把它唤醒?

在等待数据的链表xTasksWaitingToReceive,有很多个任务,有的优先级高,有的等待时间长。

谁优先级高唤醒谁,

如果优先级都相同,谁等待时间长,就唤醒谁。

以贴砖工人和拉砖司机作比喻,对比如下:

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

刚举的例子是:任务二想去读数据,没有数据就休眠。

反过来也是一样的: 任务1想去写队列,队列已经满了,他也可以休眠等待。

等待什么?等待别的任务去读队列,空出一个空间。

有学员反馈有点晕,通过直播来给大家演示一下。

直播回放:本晚课笔记所在目录后面

直播总结图:

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

五. 晚课学员提问

1. 问: 写队列,如果这个任务的优先级最高,会立即唤醒吗?

答: 假设task2读队列,因为没有数据正在休眠,

task1写队列,

task2优先级比task1高,

task2会立刻被唤醒、立刻执行。

一定要记住,实时操作系统,事关生死,高优先级的任务一旦就绪会立刻抢占低优先级的任务。

2. 问: 假设我在中断中写队列,读队列的任务在等待。那是立刻抢占还是等中断退出再抢占?

答: 中断退出之后,最高优先级的任务才可以执行。

任务不能够抢占中断,任务只有在中断处理完才可以执行。

3. 问: 如果任务因为等待队列被挂在xTasksWaitingToReceivedelay_list 时去删除这个任务,到时候xTasksWaitingToReceive怎么释放呢?

答: 删除任务之前 ,会把自己从xTasksWaitingToReceive这个链表删除掉。

4. 问: 一个任务想去读队列,但是队列里没有数据,就休眠,这个休眠的时间可以选择吗?

答: 可以选择。

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

0:没有数据我就返回一个错误,即刻返回。

portMAX_DELAY:没有数据我就永远等待,能够返回一定是已经得到的数据。

其他值:没有数据我就等待一会,如果一直没有数据的话,时间到了我就返回。

所以你要去判断返回值,成功就表示得到了数据,失败就表示没有数据。

5. 问: 唤醒等待数据的链表xTasksWaitingToReceive中的任务,要遍历一遍链表才能找到吗,还是放的时候就是按顺序的?

答: 放入链表的时候就要排序,优先级高的放在最前面、后面来等待的就放到后面去。

6. 问: 如果有多个任务想要这个数据,那么是只有一个任务才能拿到数据?

答: 是的,只有一个任务能拿到数据,也只有一个任务被唤醒。

7. 问: 老师,触发调度是通过tick中断还是唤醒后立马调度一次,这个中断是什么中断啊?

答: RTOS有些高优先级任务事关生死,高优先级的任务怎么可能等到下一个tick到来才去执行,因此FreeRTOS使用另一个中断来执行调度。

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

怎么触发调度、怎么调度,先不用管,

只要知道:task1唤醒了某个任务,如果那个任务的优先级更高,肯定会立刻发生调度,否则就不叫做实时操作系统了。

8. 问: 老师假如唤醒的是同等优先级的任务,是不是就是等待tick去触发调度了?

答: 是的。当前任务正在运行,你刚刚被唤醒,大家优先级都一样,不能抢占当前同优先级任务的时间。

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一:作业讲解
  • 二:队列和环形缓冲区
  • 三:队列——传输数据
  • 四:队列——同步任务
  • 五. 晚课学员提问
    • 1. 问: 写队列,如果这个任务的优先级最高,会立即唤醒吗?
      • 2. 问: 假设我在中断中写队列,读队列的任务在等待。那是立刻抢占还是等中断退出再抢占?
        • 3. 问: 如果任务因为等待队列被挂在xTasksWaitingToReceive和 delay_list 时去删除这个任务,到时候xTasksWaitingToReceive怎么释放呢?
          • 4. 问: 一个任务想去读队列,但是队列里没有数据,就休眠,这个休眠的时间可以选择吗?
            • 5. 问: 唤醒等待数据的链表xTasksWaitingToReceive中的任务,要遍历一遍链表才能找到吗,还是放的时候就是按顺序的?
              • 6. 问: 如果有多个任务想要这个数据,那么是只有一个任务才能拿到数据?
                • 7. 问: 老师,触发调度是通过tick中断还是唤醒后立马调度一次,这个中断是什么中断啊?
                  • 8. 问: 老师假如唤醒的是同等优先级的任务,是不是就是等待tick去触发调度了?
                  相关产品与服务
                  云直播
                  云直播(Cloud Streaming Services,CSS)为您提供极速、稳定、专业的云端直播处理服务,根据业务的不同直播场景需求,云直播提供了标准直播、快直播、云导播台三种服务,分别针对大规模实时观看、超低延时直播、便捷云端导播的场景,配合腾讯云视立方·直播 SDK,为您提供一站式的音视频直播解决方案。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档