当前位置: 首页 > news >正文

【学习日记】【FreeRTOS】延时列表的实现

前言

本文在前面文章的基础上实现了延时列表,取消了 TCB 中的延时参数。
本文是对野火 RTOS 教程的笔记,融入了笔者的理解,代码大部分来自野火。

一、如何更高效地查找延时到期的任务

1. 朴素方式

  • 在本文之前,我们使用了一种朴素的思想进行延时任务的查找:
    • 在 TCB 中设置一个延时参数,需要延时的时候进行初始化
    • 将延时任务挂起(清除就绪优先级位 uxTopReadyPriority)
    • 当 SysTick 中断时,扫描就绪列表中每个 TCB,如果延时参数不为 0 就减 1
    • 如果延时参数被减到 0,就置对应的就绪优先级位 uxTopReadyPriority,然后进行任务切换
      可以看到,上面这种想法非常朴素,但是每次 SysTick 中断的时候都需要扫描一遍就绪列表中的所有任务,当任务多的时候,耗时将会很多。

2. 更高效的方式

  • 设置除就绪列表外的另一个列表——延时列表
  • 当任务要进入延时的时候,将延时到期的值设置为节点的排序值,根据排序值按升序插入延时列表中,然后将该任务从就绪列表中删除
  • 同时更新下一个任务的解锁时刻的变量 xNextTaskUnblockTime,这个变量的意思是,当系统时基计数器 xTickCount 的值与 xNextTaskUnblockTime 相等时,就表示有任务延时到期了,需要将该任务就绪
    可以看到,FreeRTOS 用这种方式避免了扫描所有任务的延时,这点是优于 RT-Thread 和 μC/OS 的。

实际上,有两个延时列表,这是为了解决延时时间溢出的问题。

如图:
在这里插入图片描述

二、代码详解

我们添加或修改以下的代码:

  • 两条延时列表的定义和初始化
  • 下一任务到期时间点变量的定义及初始化
  • 修改延时函数,延时时将任务从就绪列表中删除并添加到延时列表
  • 修改时基计数器中断,每次计时时查看是否有任务到期

还有一些辅助的函数,主要是解决当计时溢出或者延时溢出时两条延时列表的切换:

  • 切换当前延时列表指针和溢出延时列表指针函数
  • 更新任务到期时间点变量的函数

1. 延时列表的定义及初始化

① 定义

  • 定义了两个任务延时列表,当系统时基计数器xTickCount 没有溢出时,用一条列表,当 xTickCount 溢出后,用另外一条列表
  • pxDelayedTaskList 指向 xTickCount 没有溢出时使用的那条列表
  • pxOverflowDelayedTaskList 指向 xTickCount 溢出时使用的那条列表
//延时列表
static List_t xDelayedTaskList1;
static List_t xDelayedTaskList2;
//延时列表指针(用于切换)
static List_t * volatile pxDelayedTaskList;
static List_t * volatile pxOverflowDelayedTaskList;

② 初始化

/* 初始化任务相关的列表 */
void prvInitialiseTaskLists( void )
{UBaseType_t uxPriority;//就绪列表初始化for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ ){vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );	//初始化每个就绪列表}//延时列表初始化vListInitialise( &xDelayedTaskList1 );vListInitialise( &xDelayedTaskList2 );pxDelayedTaskList = &xDelayedTaskList1;pxOverflowDelayedTaskList = &xDelayedTaskList2;
}

2. 下一任务到期时间点变量的定义和初始化

  • xNextTaskUnblockTime 用于表示下一个任务的解锁时刻
  • xNextTaskUnblockTime = xTickCount + xTicksToDelay(当前时间 + 延时时间)
  • 当系统时基计数器 xTickCount 的值与 xNextTaskUnblockTime 相等时,就表示有任务延时到期了,需要将该任务就绪

① 定义

//下一个延时任务到期的时间
static volatile TickType_t xNextTaskUnblockTime		= ( TickType_t ) 0U;

② 初始化

  • 在 vTaskStartScheduler 任务调度器函数中初始化为 portMAX_DELAY
  • portMAX_DELAY 是一个 portmacro.h 中定义的宏,默认为 0xffffffffUL
#define portMAX_DELAY ( TickType_t ) 0xffffffffULvoid vTaskStartScheduler( void )
{
/*======================================创建空闲任务start==============================================*/     TCB_t *pxIdleTaskTCBBuffer = NULL;StackType_t *pxIdleTaskStackBuffer = NULL;uint32_t ulIdleTaskStackSize;/* 获取空闲任务的内存:任务栈和任务TCB */vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize );    xIdleTaskHandle = xTaskCreateStatic( (TaskFunction_t)prvIdleTask,              /* 任务入口 */(char *)"IDLE",                           /* 任务名称,字符串形式 */(uint32_t)ulIdleTaskStackSize ,           /* 任务栈大小,单位为字 */(void *) NULL,                            /* 任务形参 */(UBaseType_t) tskIDLE_PRIORITY,           /* 任务优先级,数值越大,优先级越高 */(StackType_t *)pxIdleTaskStackBuffer,     /* 任务栈起始地址 */(TCB_t *)pxIdleTaskTCBBuffer );           /* 任务控制块 */
/*======================================创建空闲任务end================================================*/ xNextTaskUnblockTime = portMAX_DELAY;xTickCount = ( TickType_t ) 0U;/* 启动调度器 */if( xPortStartScheduler() != pdFALSE ){/* 调度器启动成功,则不会返回,即不会来到这里 */}
}

3. 延时函数的修改

任务调用延时函数时,将任务从就绪列表中转移到延时列表中:

void vTaskDelay( const TickType_t xTicksToDelay )
{TCB_t *pxTCB = NULL;/* 获取当前任务的TCB */pxTCB = pxCurrentTCB;/* 将任务插入到延时列表 */prvAddCurrentTaskToDelayedList( xTicksToDelay );/* 任务切换 */taskYIELD();
}
  • 任务插入延时列表使用 prvAddCurrentTaskToDelayedList()
  • 这个函数除了插入操作还处理了延时溢出的情况
  • 笔者画了个流程图方便大家理解:
    在这里插入图片描述
//将任务插入到延时列表
static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait )
{TickType_t xTimeToWake;/* 获取系统时基计数器xTickCount的值 */const TickType_t xConstTickCount = xTickCount;/* 将任务从就绪列表中移除 */if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ){/* 将任务在优先级位图中对应的位清除 */portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );}/* 计算延时到期时,系统时基计数器xTickCount的值是多少 */xTimeToWake = xConstTickCount + xTicksToWait;/* 将延时到期的值设置为节点的排序值 */listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );/* 溢出 */if( xTimeToWake < xConstTickCount ){vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );}else /* 没有溢出 */{vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );/* 更新下一个任务解锁时刻变量xNextTaskUnblockTime的值 */if( xTimeToWake < xNextTaskUnblockTime ){xNextTaskUnblockTime = xTimeToWake;}}	
}

4. 修改时基计数器中断

  • 修改时基计数器中断,每次计时时查看是否有任务到期
  • 并且处理时基计数器溢出情况
  • 函数流程图如下
    在这里插入图片描述
  • 函数代码如下:
//系统时基计数
void xTaskIncrementTick( void )
{TCB_t * pxTCB;TickType_t xItemValue;//系统时基计数 + 1const TickType_t xConstTickCount = xTickCount + 1;xTickCount = xConstTickCount;/* 如果xConstTickCount溢出,则切换延时列表 */if( xConstTickCount == ( TickType_t ) 0U ){taskSWITCH_DELAYED_LISTS();}/* 最近的延时任务延时到期 */if( xConstTickCount >= xNextTaskUnblockTime ){for( ;; ){if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ){/* 延时列表为空,设置xNextTaskUnblockTime为可能的最大值 */xNextTaskUnblockTime = portMAX_DELAY;break;}else /* 延时列表不为空 */{pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );/* 直到将延时列表中所有延时到期的任务移除才跳出for循环 */if( xConstTickCount < xItemValue ){xNextTaskUnblockTime = xItemValue;break;}/* 将任务从延时列表移除,消除等待状态 */( void ) uxListRemove( &( pxTCB->xStateListItem ) );/* 将解除等待的任务添加到就绪列表 */prvAddTaskToReadyList( pxTCB );}}}/* xConstTickCount >= xNextTaskUnblockTime *//* 任务切换 */portYIELD();
}

5. 交换非溢出延时指针和溢出延时指针

  • 使用一个中间变量进行交换
  • 切换延时列表后记得更新 xNextTaskUnblockTime 的值
/* * 当系统时基计数器溢出的时候,延时列表pxDelayedTaskList 和* pxOverflowDelayedTaskList要互相切换*/
#define taskSWITCH_DELAYED_LISTS()\
{\List_t *pxTemp;\pxTemp = pxDelayedTaskList;\pxDelayedTaskList = pxOverflowDelayedTaskList;\pxOverflowDelayedTaskList = pxTemp;\xNumOfOverflows++;\prvResetNextTaskUnblockTime();\
}

6. 更新任务到期时间点变量的函数

  • 函数流程图:
    在这里插入图片描述

  • 函数代码:

//重新设置变量xNextTaskUnblockTime的值
static void prvResetNextTaskUnblockTime( void )
{TCB_t *pxTCB; // 定义一个指向TCB_t类型的指针变量pxTCBif( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ){/* 新的延时任务列表为空。将xNextTaskUnblockTime设置为最大可能值,以确保在延时列表中有任务项之前,if( xTickCount >= xNextTaskUnblockTime )的测试不会通过。 */xNextTaskUnblockTime = portMAX_DELAY;}else{/* 新的延时任务列表不为空,获取延时列表头部任务的值。这个值表示了延时列表头部任务应该从阻塞状态中解除的时间。 */( pxTCB ) = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );xNextTaskUnblockTime = listGET_LIST_ITEM_VALUE( &( ( pxTCB )->xStateListItem ) );}
}

后记

如果您觉得本文写得不错,可以点个赞激励一下作者!
如果您发现本文的问题,欢迎在评论区或者私信共同探讨!
共勉!

相关文章:

【学习日记】【FreeRTOS】延时列表的实现

前言 本文在前面文章的基础上实现了延时列表&#xff0c;取消了 TCB 中的延时参数。 本文是对野火 RTOS 教程的笔记&#xff0c;融入了笔者的理解&#xff0c;代码大部分来自野火。 一、如何更高效地查找延时到期的任务 1. 朴素方式 在本文之前&#xff0c;我们使用了一种朴…...

​LeetCode解法汇总833. 字符串中的查找与替换

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 描述&#xff1a; 你会得到一…...

ide internal errors【bug】

ide internal errors【bug】 前言版权ide internal errors错误产生相关资源解决1解决2 设置虚拟内存最后 前言 2023-8-15 12:36:59 以下内容源自《【bug】》 仅供学习交流使用 版权 禁止其他平台发布时删除以下此话 本文首次发布于CSDN平台 作者是CSDN日星月云 博客主页是h…...

阿里云与中国中医科学院合作,推动中医药行业数字化和智能化发展

据相关媒体消息&#xff0c;阿里云与中国中医科学院的合作旨在推动中医药行业的数字化和智能化发展。随着互联网的进步和相关政策的支持&#xff0c;中医药产业受到了国家的高度关注。这次合作将以“互联网 中医药”为载体&#xff0c;致力于推进中医药文化的传承和创新发展。…...

【Redis】Redis 的学习教程(五)之 SpringBoot 集成 Redis

在前几篇文章中&#xff0c;我们详细介绍了 Redis 的一些功能特性以及主流的 java 客户端 api 使用方法。 在当前流行的微服务以及分布式集群环境下&#xff0c;Redis 的使用场景可以说非常的广泛&#xff0c;能解决集群环境下系统中遇到的不少技术问题&#xff0c;在此列举几…...

github以及上传代码处理

最近在github上传代码的时候出现了&#xff1a; /video_parser# git push -u origin main Username for https://github.com: gtnyxxx Password for https://gtny2010github.com: remote: Support for password authentication was removed on August 13, 2021. remote: Plea…...

【PACS源码】认识PACS的架构和工作流程

&#xff08;一&#xff09;PACS系统的组成及架构 PACS系统的基本组成部分包括&#xff1a;数字影像采集、通讯和网络、医学影像存储、医学影像管理、各类工作站五个部分。 而目前PACS系统的软件架构选型上看&#xff0c;主要有C/S和B/S两种形式。 C/S架构&#xff0c;即Client…...

【C++】开源:跨平台Excel处理库-libxlsxwriter配置使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍Excel处理库-libxlsxwriter配置使用。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&…...

前端-轮询

一、轮询定义 轮询是指在一定的时间间隔内&#xff0c;定时向服务器发送请求&#xff0c;获取最新数据的过程。轮询通常用于从服务器获取实时更新的数据。 二、轮询和长轮询区别 轮询是在固定的时间间隔内向服务器发送请求&#xff0c;即使服务器没有数据更新也会继续发送请求…...

Python “贪吃蛇”游戏,在不断改进中学习pygame编程

目录 前言 改进过程一 增加提示信息 原版帮助摘要 pygame.draw pygame.font class Rect class Surface 改进过程二 增加显示得分 改进过程三 增加背景景乐 增加提示音效 音乐切换 静音切换 mixer.music.play 注意事项 原版帮助摘要 pygame.mixer pygame.mix…...

Linux网络编程_Ubuntu环境配置安装

文章目录&#xff1a; 一&#xff1a;基于vmware虚拟机安装Ubuntu系统&#xff08;虚拟机&#xff09; 1.vmware下载 2.Ubuntu系统下载 3.配置 3.1 无法连网&#xff1a;这里很容易出现问题 3.2 更换国内源 3.3 无法屏幕适配全屏 3.4 汉化 二&#xff1a;直接安装Ubun…...

gradle java插件

gradle java插件 1. 由来 Gradle是一种现代化的构建工具&#xff0c;Java插件是Gradle官方提供的插件&#xff0c;用于支持和管理Java项目的构建过程。 2. 常见五种示例和说明 示例1&#xff1a;配置源代码目录和编译选项 plugins {id java }sourceSets {main {java {srcD…...

神经网络基础-神经网络补充概念-48-rmsprop

概念## 标题 RMSProp&#xff08;Root Mean Square Propagation&#xff09;是一种优化算法&#xff0c;用于在训练神经网络等机器学习模型时自适应地调整学习率&#xff0c;以加速收敛并提高性能。RMSProp可以有效地处理不同特征尺度和梯度变化&#xff0c;对于处理稀疏数据和…...

分析Flink,源和算子并行度不一致时,运行一段时间后,看似不再继续消费的问题,提供解决思路。

文章目录 背景分析 问题来了比较一开始的情况解决方式 背景 之前有分析过一次类似问题&#xff0c;最终结论是在keyby之后&#xff0c;其中有一个key数量特别庞大&#xff0c;导致对应的subtask压力过大&#xff0c;进而使得整个job不再继续运作。在这个问题解决之后&#xff…...

PyTorch训练深度卷积生成对抗网络DCGAN

文章目录 DCGAN介绍代码结果参考 DCGAN介绍 将CNN和GAN结合起来&#xff0c;把监督学习和无监督学习结合起来。具体解释可以参见 深度卷积对抗生成网络(DCGAN) DCGAN的生成器结构&#xff1a; 图片来源&#xff1a;https://arxiv.org/abs/1511.06434 代码 model.py impor…...

Spring-4-掌握Spring事务传播机制

今日目标 能够掌握Spring事务配置 Spring事务管理 1 Spring事务简介【重点】 1.1 Spring事务作用 事务作用&#xff1a;在数据层保障一系列的数据库操作同成功同失败 Spring事务作用&#xff1a;在数据层或业务层保障一系列的数据库操作同成功同失败 1.2 案例分析Spring…...

[PyTorch][chapter 49][创建自己的数据集 1]

前言&#xff1a; 后面几章主要利用DataSet 创建自己的数据集&#xff0c;实现建模&#xff0c; 训练&#xff0c;迁移等功能。 目录: pokemon 数据集深度学习工程步骤 一 pokemon 数据集介绍 1.1 pokemon: 数据集地址&#xff1a; 百度网盘路径: https://pan.baidu.com/s/1…...

中间件(二)dubbo负载均衡介绍

一、负载均衡概述 支持轮询、随机、一致性hash和最小活跃数等。 1、轮询 ① sequences&#xff1a;内部的序列计数器 ② 服务器接口方法权重一样&#xff1a;&#xff08;sequences1&#xff09;%服务器的数量&#xff08;决定调用&#xff09;哪个服务器的服务。 ③ 服务器…...

springboot异步文件上传获取输入流提示找不到文件java.io.FileNotFoundException

springboot上传文件&#xff0c;使用异步操作处理上传的文件数据&#xff0c;出现异常如下&#xff1a; 这个是在异步之后使用传过来的MultipartFile对象尝试调用getInputStream方法发生的异常。 java.io.FileNotFoundException: C:\Users\Administrator\AppData\Local\Temp\to…...

安装jenkins-cli

1、要在 Linux 操作系统上安装 jcli curl -L https://github.com/jenkins-zh/jenkins-cli/releases/latest/download/jcli-linux-amd64.tar.gz|tar xzv sudo mv jcli /usr/local/bin/ 在用户根目录下&#xff0c;增加 jcli 的配置文件&#xff1a; jcli config gen -ifalse …...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

Qt Http Server模块功能及架构

Qt Http Server 是 Qt 6.0 中引入的一个新模块&#xff0c;它提供了一个轻量级的 HTTP 服务器实现&#xff0c;主要用于构建基于 HTTP 的应用程序和服务。 功能介绍&#xff1a; 主要功能 HTTP服务器功能&#xff1a; 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

C++:多态机制详解

目录 一. 多态的概念 1.静态多态&#xff08;编译时多态&#xff09; 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1&#xff09;.协变 2&#xff09;.析构函数的重写 5.override 和 final关键字 1&#…...

LLMs 系列实操科普(1)

写在前面&#xff1a; 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容&#xff0c;原视频时长 ~130 分钟&#xff0c;以实操演示主流的一些 LLMs 的使用&#xff0c;由于涉及到实操&#xff0c;实际上并不适合以文字整理&#xff0c;但还是决定尽量整理一份笔…...

【JVM】Java虚拟机(二)——垃圾回收

目录 一、如何判断对象可以回收 &#xff08;一&#xff09;引用计数法 &#xff08;二&#xff09;可达性分析算法 二、垃圾回收算法 &#xff08;一&#xff09;标记清除 &#xff08;二&#xff09;标记整理 &#xff08;三&#xff09;复制 &#xff08;四&#xff…...

省略号和可变参数模板

本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...