(学习日记)2024.03.14:UCOSIII第十六节:时间片

写在前面:
由于时间的不足与学习的碎片化,写博客变得有些奢侈。
但是对于记录学习(忘了以后能快速复习)的渴望一天天变得强烈。
既然如此
不如以天为单位,以时间为顺序,仅仅将博客当做一个知识学习的目录,记录笔者认为最通俗、最有帮助的资料,并尽量总结几句话指明本质,以便于日后搜索起来更加容易。


标题的结构如下:“类型”:“知识点”——“简短的解释”
部分内容由于保密协议无法上传。


点击此处进入学习日记的总目录

2024.03.14

  • 三十、UCOSIII:时间片
    • 1、实现时间片
      • 1. 修改任务TCB
      • 2. 实现时间片调度OS_SchedRoundRobin()函数
    • 2、修改OSTimeTick()函数
    • 3、修改OSTaskCreate()函数
    • 4、修改OS_IdleTaskInit()函数
    • 5、main()函数
    • 6、实验现象

三十、UCOSIII:时间片

本章开始,我们让OS支持同一个优先级下可以有多个任务的功能,这些任务可以分配不同的时间片,当任务时间片用完的时候, 任务会从链表的头部移动到尾部,让下一个任务共享时间片,以此循环。

1、实现时间片

1. 修改任务TCB

为了实现时间片功能,我们需要先在任务控制块TCB中添加两个时间片相关的变量

struct os_tcb {
    CPU_STK         *StkPtr;
    CPU_STK_SIZE    StkSize;

    /* 任务延时周期个数 */
    OS_TICK         TaskDelayTicks;

    /* 任务优先级 */
    OS_PRIO         Prio;

    /* 就绪列表双向链表的下一个指针 */
    OS_TCB          *NextPtr;
    /* 就绪列表双向链表的前一个指针 */
    OS_TCB          *PrevPtr;

    /*时基列表相关字段*/
    OS_TCB          *TickNextPtr;
    OS_TCB          *TickPrevPtr;
    OS_TICK_SPOKE   *TickSpokePtr;

    OS_TICK         TickCtrMatch;
    OS_TICK         TickRemain;

    /* 时间片相关字段 */
    OS_TICK              TimeQuanta;		//(1)
    OS_TICK              TimeQuantaCtr;		//(2)
};
  • (1):TimeQuanta表示任务需要多少个时间片,单位为系统时钟周期Tick。
  • (2):TimeQuantaCtr表示任务还剩下多少个时间片,每到来一个系统时钟周期, TimeQuantaCtr会减一,当TimeQuantaCtr等于零的时候,表示时间片用完, 任务的TCB会从就绪列表链表的头部移动到尾部,好让下一个任务共享时间片。

在这里插入图片描述

2. 实现时间片调度OS_SchedRoundRobin()函数

时间片调度函数OS_SchedRoundRobin()在os_core.c中实现,在OSTimeTick()调用。
在这里插入图片描述

该图画的是在一个就绪链表中, 有三个任务就绪。
其中在优先级2下面有两个任务,均分配了两个时间片,其中任务3的时间片已用完,则位于链表的末尾, 任务2的时间片还剩一个,则位于链表的头部。
当下一个时钟周期到来的时候,任务2的时间片将耗完, 相应的TimeQuantaCtr会递减为0,任务2的TCB会被移动到链表的末尾,任务3则被成为链表的头部, 然后重置任务3的时间片计数器TimeQuantaCtr的值为2,重新享有时间片。

#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u		//(1)
void OS_SchedRoundRobin(OS_RDY_LIST  *p_rdy_list)
{
    OS_TCB   *p_tcb;
    CPU_SR_ALLOC();

    /*  进入临界段 */
    CPU_CRITICAL_ENTER();

    p_tcb = p_rdy_list->HeadPtr;		//(2)

    /* 如果TCB节点为空,则退出 */
    if (p_tcb == (OS_TCB *)0) 		//(3)
    {
        CPU_CRITICAL_EXIT();
        return;
    }

    /* 如果是空闲任务,也退出 */
    if (p_tcb == &OSIdleTaskTCB) {		//(4)
        CPU_CRITICAL_EXIT();
        return;
    }

    /* 时间片自减 */
    if (p_tcb->TimeQuantaCtr > (OS_TICK)0) {		//(5)
        p_tcb->TimeQuantaCtr--;
    }

    /* 时间片没有用完,则退出 */
    if (p_tcb->TimeQuantaCtr > (OS_TICK)0) {		//(6)
        CPU_CRITICAL_EXIT();
        return;
    }

    /* 如果当前优先级只有一个任务,则退出 */
    if (p_rdy_list->NbrEntries < (OS_OBJ_QTY)2) {		//(7)
        CPU_CRITICAL_EXIT();
        return;
    }

    /* 时间片耗完,将任务放到链表的最后一个节点 */
    OS_RdyListMoveHeadToTail(p_rdy_list);		//(8)

    /* 重新获取任务节点 */
    p_tcb = p_rdy_list->HeadPtr;		//(9)
    /* 重载默认的时间片计数值 */
    p_tcb->TimeQuantaCtr = p_tcb->TimeQuanta;

    /* 退出临界段 */
    CPU_CRITICAL_EXIT();
}
#endif/* OS_CFG_SCHED_ROUND_ROBIN_EN > 0u */
  • (1):时间片是一个可选的功能, 是否选择由OS_CFG_SCHED_ROUND_ROBIN_EN控制,该宏在os_cfg.h定义。
  • (2):获取链表的第一个节点。
  • (3):如果节点为空,则退出。
  • (4):如果节点不为空,看看是否是空闲任务,如果是则退出。
  • (5):如果不是空闲任务,则时间片计数器TimeQuantaCtr减一操作。
  • (6):时间片计数器TimeQuantaCtr递减之后,则判断下时间片是否用完,如果没有用完,则退出。
  • (7):如果时间片用完,则判断性该优先级下有多少个任务,如果是一个,就退出。
  • (8):时间片用完,如果该优先级下有两个以上任务,则将刚刚耗完时间片的节点移到链表的末尾, 此时位于末尾的任务的TCB字段中的TimeQuantaCtr是等于0的,只有等它下一次运行的时候值才会重置为TimeQuanta。
  • (9):重新获取链表的第一个节点, 重置时间片计数器TimeQuantaCtr的值等于TimeQuanta,任务重新享有时间片。

2、修改OSTimeTick()函数

任务的时间片的单位在每个系统时钟周期到来的时候被更新,时间片调度函数则由时基周期处理函数OSTimeTick()调用, 只需要在更新时基列表之后调用时间片调度函数即可

void  OSTimeTick (void)
{
    /* 更新时基列表 */
    OS_TickListUpdate();

#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u
    /* 时间片调度 */
    OS_SchedRoundRobin(&OSRdyList[OSPrioCur]);
#endif

    /* 任务调度 */
    OSSched();
}

3、修改OSTaskCreate()函数

任务的时间片在函数创建的时候被指定

void OSTaskCreate (OS_TCB        *p_tcb,
                OS_TASK_PTR   p_task,
                void          *p_arg,
                OS_PRIO       prio,
                CPU_STK       *p_stk_base,
                CPU_STK_SIZE  stk_size,
                OS_TICK       time_quanta,		//(1)
                OS_ERR        *p_err)
{
    CPU_STK       *p_sp;
    CPU_SR_ALLOC();

    /* 初始化TCB为默认值 */
    OS_TaskInitTCB(p_tcb);

    /* 初始化栈 */
    p_sp = OSTaskStkInit( p_task,
                        p_arg,
                        p_stk_base,
                        stk_size );

    p_tcb->Prio = prio;

    p_tcb->StkPtr = p_sp;
    p_tcb->StkSize = stk_size;

    /* 时间片相关初始化 */
    p_tcb->TimeQuanta    = time_quanta;		//(2)
#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u
    p_tcb->TimeQuantaCtr = time_quanta;		//(3)
#endif

    /* 进入临界段 */
    OS_CRITICAL_ENTER();

    /* 将任务添加到就绪列表 */
    OS_PrioInsert(p_tcb->Prio);
    OS_RdyListInsertTail(p_tcb);

    /* 退出临界段 */
    OS_CRITICAL_EXIT();

    *p_err = OS_ERR_NONE;
}
  • (1):时间片在任务创建的时候由函数形参time_quanta指定。
  • (2):初始化任务TCB字段的时间片变量TimeQuanta, 该变量表示任务能享有的最大的时间片是多少,该值一旦初始化后就不会变,除非认为修改。
  • (3):初始化时间片计数器TimeQuantaCtr的值等于TimeQuanta, 每经过一个系统时钟周期,该值会递减,如果该值为0,则表示时间片耗完。

4、修改OS_IdleTaskInit()函数

因为在OS_IdleTaskInit()函数中创建了空闲任务,所以该函数也需要修改,只需在空闲任务创建函数中,添加一个时间片的形参就可, 时间片我们分配为0,因为在空闲任务优先级下只有空闲任务一个任务,没有其他的任务。

void  OS_IdleTaskInit(OS_ERR  *p_err)
{
    /* 初始化空闲任务计数器 */
    OSIdleTaskCtr = (OS_IDLE_CTR)0;

    /* 创建空闲任务 */
    OSTaskCreate( (OS_TCB     *)&OSIdleTaskTCB,
                (OS_TASK_PTR )OS_IdleTask,
                (void       *)0,
                (OS_PRIO)(OS_CFG_PRIO_MAX - 1u),
                (CPU_STK    *)OSCfg_IdleTaskStkBasePtr,
                (CPU_STK_SIZE)OSCfg_IdleTaskStkSize,
                (OS_TICK       )0,
                (OS_ERR     *)p_err );
}

5、main()函数

这里,我们创建任务1、2和3,其中任务1的优先级为1,时间片为0,任务2和任务3的优先级相同,均为2,均分配两个两个时间片。
当任务创建完毕后,就绪列表的分布图具体见图
在这里插入图片描述

int main(void)
{
    OS_ERR err;


    /* CPU初始化:1、初始化时间戳 */
    CPU_Init();

    /* 关闭中断 */
    CPU_IntDis();

    /* 配置SysTick 10ms 中断一次 */
    OS_CPU_SysTickInit (10);

    /* 初始化相关的全局变量 */
    OSInit(&err);

    /* 创建任务 */
    OSTaskCreate( (OS_TCB       *)&Task1TCB,
                (OS_TASK_PTR   )Task1,
                (void         *)0,
                (OS_PRIO       )1,		//(1)
                (CPU_STK      *)&Task1Stk[0],
                (CPU_STK_SIZE  )TASK1_STK_SIZE,
                (OS_TICK       )0,		//(1)
                (OS_ERR       *)&err );

    OSTaskCreate( (OS_TCB       *)&Task2TCB,
                (OS_TASK_PTR   )Task2,
                (void         *)0,
                (OS_PRIO       )2,		//(2)
                (CPU_STK      *)&Task2Stk[0],
                (CPU_STK_SIZE  )TASK2_STK_SIZE,
                (OS_TICK       )1,		//(2)
                (OS_ERR       *)&err );

    OSTaskCreate( (OS_TCB       *)&Task3TCB,
                (OS_TASK_PTR   )Task3,
                (void         *)0,
                (OS_PRIO       )2,		//(2)
                (CPU_STK      *)&Task3Stk[0],
                (CPU_STK_SIZE  )TASK3_STK_SIZE,
                (OS_TICK       )1,		//(2)
                (OS_ERR       *)&err );

    /* 启动OS,将不再返回 */
    OSStart(&err);
}

void Task1( void *p_arg )
{
    for ( ;; ) {
        flag1 = 1;
        OSTimeDly(2);
        flag1 = 0;
        OSTimeDly(2);
    }
}

void Task2( void *p_arg )
{
    for ( ;; ) {
        flag2 = 1;
        //OSTimeDly(1);		//(3)
        delay(0xff);
        flag2 = 0;
        //OSTimeDly(1);
        delay(0xff);
    }
}

void Task3( void *p_arg )
{
    for ( ;; ) {
        flag3 = 1;
        //OSTimeDly(1);		//(3)
        delay(0xff);
        flag3 = 0;
        //OSTimeDly(1);
        delay(0xff);
    }
}
  • (1):任务1的优先级为1,时间片为0。当同一个优先级下有多个任务的时候才需要时间片功能。
  • (2):任务2和任务3的优先级相同,均为2,且分配相同的时间片,时间片也可以不同。
  • (3):因为任务2和3的优先级相同,分配了相同的时间片,也可以分配不同的时间片, 并把阻塞延时换成软件延时,不管是阻塞延时还是软件延时,延时的时间都必须小于时间片, 因为相同优先级的任务在运行的时候最大不能超过时间片的时间。

6、实验现象

进入软件调试,单击全速运行按钮就可看到实验波形,具体见图
在这里插入图片描述
在图中我们可以看到,在任务1的flag1置1和置0的两个时间片内, 任务2和3都各运行了一次,运行的时间均为1个时间片,在这1个时间片内任务2和3的flag变量翻转了好多次,即任务运行了好多次。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/473918.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

编曲学习:如何编写钢琴织体 Cubase12逻辑预置 需要弄明白的问题

钢琴织体是指演奏形式、方式,同一个和弦进行可以用很多种不同的演奏方法。常用织体有分解和弦,柱式和弦,琶音织体,混合织体。 在编写钢琴织体前,先定好歌曲的调。 Cubase小技巧:把钢琴轨道向上拖动打和弦轨道,就可以显示和弦!如果你有一些参考工程,不知道用了哪些和…

PG中一个表的3~8个文件是怎么来的?

个人简介 微信公众号&#xff1a;数据库杂记 个人微信: _iihero 我是iihero. 也可以叫我Sean. iiheroCSDN(https://blog.csdn.net/iihero) Sean墨天轮 (https://www.modb.pro/u/16258) iiherozhihu (https://www.zhihu.com/people/zhou-mo-xu) 数据库领域的资深爱好者一枚。…

关于vue3的自定义hook

把一些逻辑写在单独的ts文件里&#xff0c;vue文件使用的时候引入即可 计算属性 1、方法1&#xff1a;return计算属性 直接写出去的话&#xff0c;使用时报错 ExpirationTime是一个计算属性&#xff0c;若直接在html上使用{{ExpirationTime(orderForm)}}报错 查阅资料显示&…

Go --- Go语言垃圾处理

概念 垃圾回收&#xff08;GC-Garbage Collection&#xff09;暂停程序业务逻辑SWT&#xff08;stop the world&#xff09;程序根节点&#xff1a;程序中被直接或间接引用的对象集合&#xff0c;能通过他们找出所有可以被访问到的对象&#xff0c;所以Go程序的根节点通常包括…

UE5的渲染-太难了

大家可以看到&#xff0c;这些都是UE的渲染&#xff0c;非常漂亮惊叹&#xff0c;渲染已经非常成熟&#xff0c;这些画面并不是离线渲染&#xff0c;而是实时渲染。早先年我们渲染CG动画都采用离线渲染&#xff0c;要用到庞大的渲染农场&#xff0c;每渲染一帧都可能需要半个小…

【Maven】使用maven-jar、maven-assembly、maven-shade优雅的实现第三方依赖一同打Jar包

文章目录 一.前言二.常规Jar 打包&#xff1a;maven-jar-plugin三.Shade 打包&#xff1a;maven-shade-plugin1.如何使用2.将部分jar包添加或排除3.将依赖jar包内部资源添加或排除4.自动将所有不使用的类排除5.将依赖的类重命名并打包进来 &#xff08;隔离方案&#xff09;6.修…

VMware中UbuntuServer扩展硬盘空间

VMware中UbuntuServer扩展硬盘空间 没有不可治愈的伤痛&#xff0c;没有不能结束的沉沦&#xff0c;所有失去的&#xff0c;会以另一种方式归来 ——【约翰-肖尔斯】 第一步 lxalxa:~$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS loop0 …

从边缘设备丰富你的 Elasticsearch 文档

作者&#xff1a;David Pilato 我们在之前的文章中已经了解了如何丰富 Elasticsearch 本身和 Logstash 中的数据。 但如果我们可以从边缘设备中做到这一点呢&#xff1f; 这将减少 Elasticsearch 要做的工作。 让我们看看如何从具有代理处理器的 Elastic 代理中执行此操作。 E…

JDK,Nginx,Redis安装

创建develop目录 mkdir /usr/local/develop/ cd /usr/local/develop 下载 wget http://nginx.org/download/nginx-1.17.4.tar.gz yum install git git clone https://github.com/arut/nginx-rtmp-module.git 解压文件 tar zxmf nginx-1.17.4.tar.gz 进入解压目录 cd ng…

flutter 局部view更新,dialog更新进度,dialog更新

局部更新有好几种方法&#xff0c;本次使用的是 StatefulBuilder 定义 customState去更新对话框内容 import package:flutter/cupertino.dart; import package:flutter/material.dart;class ProgressDialog {final BuildContext context;BuildContext? dialogContext;double _…

关系型数据库mysql(2)SQL语句

目录 一.SQL语句简介 1.1SQL语言 1.2SQL语句分类 1.3SQL分类 1.4SQL 语言规范 二.数据库基本操作 2.1查看数据库中的库信息 2.2查看数据库中的表信息 数据库内查看 数据库外查看 2.3显示数据库的结构&#xff08;字段&#xff09; ​编辑 2.4 字段属性 2.5常见的数…

音频和视频标签

音频用audio标签 controls表示控制栏 loop循环播放音频 autoplay自动播放&#xff08;浏览器基于隐私一般不支持&#xff09; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Com…

sonar接入maven项目

1、介绍 sonar是一款静态代码质量分析工具&#xff0c;支持Java、Python、PHP、JavaScript、CSS等25种以上的语言&#xff0c;而且能够集成在IDE、Jenkins、Git等服务中&#xff0c;方便随时查看代码质量分析报告。他有如下特性 (1) 检查代码是否遵循编程标准&#xff1a;如命…

[BT]BUUCTF刷题第2天(3.20)

第2天&#xff08;共5题&#xff09; Web [ACTF2020 新生赛]Exec Payload&#xff1a;target127.0.0.1;cat /flag 分号;在许多shell中用作命令分隔符&#xff0c;意味着在执行完前一个命令&#xff08;这里是设置target变量&#xff09;后&#xff0c;接着执行cat /flag命令…

Spark-Scala语言实战(4)

在之前的文章中&#xff0c;我们学习了如何在scala中定义无参&#xff0c;带参以及匿名函数。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&#xff0c;谢谢。 Spark-Scala语言…

IDEA 下载依赖包源码报错 Cannot download sources Sources not found for:XXX

最近在做一个功能的时候想看一个库的源码&#xff0c;结果源码下不下来&#xff0c;报Cannot download sources Sources not found for:XXX,网上搜了半天&#xff0c;也找不到靠谱的结论 后来想了下&#xff0c;应该是镜像那边出了问题&#xff0c;把镜像一删&#xff0c;源码…

航空实时监控

1、从Kafka中读取飞机数据&#xff0c;并进行清洗 此步骤在前面的“使用Spark清洗统计业务数据并保存到数据库中”任务阶段应该已经完成。如果没有完成&#xff0c;请参考源代码自行完成。核心类主要有三个&#xff1a;SparkStreamingApplication类、SparkUtil类和MapManager类…

免费PDF转换和编辑工具 PDFgear 2.1.4

PDFgear是一款功能强大的 PDF 阅读及转换软件。 它支持多种文件格式的转换和编辑&#xff0c;同时还提供了丰富的功能模块&#xff0c;如签名、表单填写等&#xff0c;方便用户进行多样化的操作。 该软件界面简洁美观&#xff0c;操作简单易懂&#xff0c;适合不同层次的用户…

从零到一构建短链接系统(八)

1.git上传远程仓库&#xff08;现在才想起来&#xff09; git init git add . git commit -m "first commit" git remote add origin OLiyscxm/shortlink git push -u origin "master" 2.开发全局异常拦截器之后就可以简化UserController 拦截器可以…

ASP .Net Core 配置集合 IConfiguration 的使用

&#x1f433;简介 IConfiguration 是 ASP.NET Core 中的一个接口&#xff0c;用于表示配置集合。以下是关于 IConfiguration 的详细介绍&#xff1a; 作用&#xff1a;IConfiguration 允许开发人员从各种来源&#xff08;如文件、环境变量、命令行参数等&#xff09;读取应用…