FreeRTOS中级篇

2023-05-16

一、内存管理

1、五种内存管理模式

FreeRTOS提供了5种内存管理实现方法,可以根据不同的使用场合选择不同的模式。
在这里插入图片描述
关于5中内存管理模式可以看下面这篇文章。
链接: https://zhuanlan.zhihu.com/p/115276865

默认情况下,FreeRTOS内核创建的任务、队列、信号量、事件组等都是借助内存管理函数从内存堆中分配内存。对于1、2、4这三种模式,内存中的堆实际上是一个很大的数组。创建的任务TCB结构体、任务栈实际上都保存在这块大数组上。
在这里插入图片描述
在这里插入图片描述
默认堆的大小是17kb。

2、任务分配空间

在这里插入图片描述
可以看到,创建的任务TCB结构体和任务的栈都在堆上。使用xTaskCreate()动态创建任务时,FreeRTOS会在堆中开辟出一块空间,存放任务的TCB结构体和栈。每个任务都有各自独立的内存空间,互相独立。调用xTaskCreateStatic()函数在静态区建立任务。

我们目前只需要知道,除了heap_3模式使用的是标准库里的malloc和free函数,其他的都是FreeRTOS自己提供的申请、释放内存函数。
在heap_4这种常用模式,动态申请的堆实际上是从内部的一个巨大的数组中申请的。

二、TCB结构体

每个任务都会有自己的TCB结构体,用来保存自己的一些信息。下面是删除配置项之后的TCB结构体。

typedef struct tskTaskControlBlock
{
	/* 指向任务堆栈最后一项位置,也就是栈顶,为了便于上下文切换操作,这里栈顶指针必须位于TCB第一项 */
	volatile StackType_t * pxTopOfStack; 
	/* 任务状态,不同的状态会添加到不同的状态链表,如就绪链表*/
	ListItem_t xStateListItem;  
	/* 事件链表项,会添加到不同的事件链表下 */
	ListItem_t xEventListItem; 
	/* 任务优先级,数值越大优先级越高 */
	UBaseType_t uxPriority;    
	/* 申请到的任务栈起始地址,也就是栈底,可以用来检测堆栈是否溢出 */         
	StackType_t * pxStack;        
	/* 任务名,默认16个字节 */    
	char pcTaskName[ configMAX_TASK_NAME_LEN ]; 
} tskTCB;

对比任务创建函数:
在这里插入图片描述
可以看到,TCB结构体并没有保存任务的函数指针和参数,那么创建任务时传入的函数指针和参数在哪里呢?
我们知道,每个任务都有自己的栈空间,用来保存内部寄存器的数据。在创建任务的时候,程序会修改栈空间里面对应寄存器的值,恢复运行的时,pc寄存器恢复为该任务的函数地址;R0-R3寄存器恢复为函数的参数。所以TCB结构体并不需要保存函数指针和参数。
每个任务的栈,需要自根据实际情况确定,关键是局部变量的大小和函数调用的深度。
在这里插入图片描述

三、任务创建流程

1、创建临时TCB结构体和返回值。
在这里插入图片描述
2、根据栈的增长方式决定TCB结构体和栈空间的申请顺序。
如果堆栈增长方式是从低地址向高地址增加,则先分配TCB然后分配堆栈。
在这里插入图片描述
如果堆栈增长方式是高地址往低地址增加,则先分配堆栈然后分配TCB,以便堆栈不会增长到TCB。
在这里插入图片描述
为什么要区别栈的增长方式来决定栈和TCB的分配顺序呢?

因为一个任务的任务栈和TCB是紧挨着的,TCB的大小是固定的,任务栈的大小是变化的。在这里插入图片描述
申请TCB和栈都是在堆上申请的,堆上先申请低地址空间,后申请高地址空间。任务栈访问的方式有向下增长和向上增长两种方式。
如果栈向下增长,也就是栈顶在高地址,栈底子在低地址。此时,如果先申请TCB后申请任务栈,内存分布图如下:
在这里插入图片描述
在这种情况下,使用任务栈的时候,栈顶指针往低地址走,如果创建任务时分配的栈空间的大小不够的话,那么栈指针可能会越过栈底,去操作TCB结构体的部分,这样任务的TCB就会被破坏,导致无法估计的错误。
而如果先申请任务栈再申请TCB,内存分布图如下:
在这里插入图片描述
这种情况下,栈顶指针往向低地址走,即使任务栈空间不足,也不会触碰到该任务的TCB区域,这样相对安全一些。但是如果他前面还有别的任务的堆栈空间,也可能会把前面任务的堆栈空间破坏。
在这里插入图片描述
所以,在创建任务的时候,一定要保证任务的栈空间时足够该任务使用的。
3、初始化TCB结构体和确定返回值。
在这里插入图片描述
经过以上三步,任务就创建完成了。

四、任务管理

1、任务链表

被创建出来的任务保存在哪里,执行的时候又怎么取出来呢?
从上面的任务创建过程中我们知道,任务创建出来之后会被放入就绪列表中。
在这里插入图片描述
进入此函数内部看看。
在这里插入图片描述
在函数里面又调用了一个函数prvAddTaskToReadyList,查看该函数。
在这里插入图片描述
根据函数名,可以推测出这个函数功能是根据新任务的优先级和任务状态把新创建的任务插入到对应的链表的最末尾。
任务的链表有三种,就绪链表、阻塞链表、挂起链表。分别存放就绪态任务、阻塞态任务、挂起态任务。其中就绪链表的数量由系统的优先级数量级决定。
在这里插入图片描述在这里插入图片描述
综上可知,后被创建的任务会被放到对应链表的最后,也就是尾部。
在这里插入图片描述
注意:空闲任务函数也在就绪链表中。
任务调度就只负责调度就绪链表中的任务,根据优先级由高到低执行任务,执行完毕后将任务放到对应就绪链表尾部。
当任务状态变为阻塞态或者挂起态,任务就会从就绪链表移动到阻塞链表或者挂起链表中,CPU只执行就绪链表中的任务,不会执行其他链表中的任务,所以挂起链表和阻塞链表中的任务不占用CPU资源。当任务由阻塞链表或挂起链表移动到就绪链表后,会发起新的调度,CPU就会从上到下根据优先级搜索就绪链表中的任务然后开始执行。

2、任务调度

在每个tick中断中,会调用tick中断服务函数。在tick中断服务函数中,会取出下一个任务,然后切换任务,在切换任务时,又会保存当前任务,恢复新任务。
在这里插入图片描述

3、同优先级任务执行顺序

在创建任务时,新任务会被函数添加到就绪链表中,在函数内部有下面这样的判断。
在这里插入图片描述
如果新任务的优先级大于等于当前任务(也就是上一个被创建的任务)的优先级,那么当前任务就等于新任务。即新任务会先执行。

3.1优先级0任务执行顺序

但是如果任务的优先级是0,与空闲任务的优先级相同,并且空闲任务配置为不礼让模式,那么最先被执行的任务就是空闲任务,空闲任务先执行,然后进行调度,重新执行就绪链表中的其他任务。如果空闲任务配置为礼让模式,那么空闲任务会在其他任务执行之后被执行。
实验代码:

#define configIDLE_SHOULD_YIELD		0	//配置空闲任务不礼让
void Task1(void * param)
{
	while(1)
	{
		Task1RunningFlag = 1;
		Task2RunningFlag = 0;
		Task3RunningFlag = 0;
		IdleRunningFlag = 0;
		printf("1");
	}
}
void Task2(void * param)
{
	while(1)
	{
		Task1RunningFlag = 0;
		Task2RunningFlag = 1;
		Task3RunningFlag = 0;
		IdleRunningFlag = 0;
		printf("2");
	}	
}
void Task3(void * param)
{
	while(1)
	{
		Task1RunningFlag = 0;
		Task2RunningFlag = 0;
		Task3RunningFlag = 1;
		IdleRunningFlag = 0;
		printf("3");
	}	
}
void vApplicationIdleHook(void)
{
	Task1RunningFlag = 0;
	Task2RunningFlag = 0;
	Task3RunningFlag = 0;
	IdleRunningFlag = 1;
	printf("0");
}
int main( void )
{
	prvSetupHardware();
	printf("Hello World!\r\n");

	xTaskCreate(Task1,"Task1",100,NULL,0,NULL);
	xTaskCreate(Task2,"Task2",100,NULL,0,NULL);
	xTaskCreate(Task3,"Task3",100,NULL,0,NULL);

	vTaskStartScheduler();
	return 0;
}

输出结果:
在这里插入图片描述
可以看到,创建的三个任务优先级为0,空闲任务优先执行,然后任务1、2、3执行。

#define configIDLE_SHOULD_YIELD		1	//配置空闲任务礼让

在这里插入图片描述
可以看到,先执行按顺序添加的任务,然后执行空闲任务。
为什么空闲任务是否礼让会有上面两种不同的运行情况?
我们知道空闲任务是在启动调度器函数中被创建的,所以空闲任务是最后一个创建的任务应该优先执行,那为什么在礼让模式下,空闲任务反而最后执行呢?
在这里插入图片描述
我们进入空闲任务函数portTASK_FUNCTION中看看。
在函数内部,我们可以看到下面这样的一个判断。
在这里插入图片描述
空闲任务是否礼让的配置宏作为判断条件,如果空闲任务礼让,那么条件成立,会执行下面的taskYIELD函数重新发起一次任务调度,任务就从就绪链表中重新选择任务执行。也就是说礼让模式下,空闲任务主动放弃一次运行机会,下一次再正常运行。如果空闲任务不礼让,条件不成立,不会发起新的调度,当前任务就是空闲任务,所以空闲任务先执行,然后在执行其他函数。
这样就解释了上面的两种现象。

3.2优先级大于0任务执行顺序

当任务的优先级大于空闲任务时,后创建的任务先执行。
main函数中的创建任务优先级改变为1。

xTaskCreate(Task1,"Task1",100,NULL,1,NULL);
xTaskCreate(Task2,"Task2",100,NULL,1,NULL);
xTaskCreate(Task3,"Task3",100,NULL,1,NULL);

输出结果:
在这里插入图片描述
可以看到,最后被创建的任务3先执行,然后在从链表头顺序执行。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FreeRTOS中级篇 的相关文章

  • FreeRTOS系列

    1 多任务系统 1 1 前后台系统 单片机裸机开发时 一般都是在main函数里面用while 1 做一个大循环来完成所有的处理 循环中调用相应的函数完成所需的处理 有时也需要在中断中完成一些处理 相对于多任务系统而言 这就是单人单任务系统也
  • FreeRTOS-内核控制函数

    FreeRTOS 内核控制函数 FreeRTOS中有一些内核函数 一般来说这些内核函数在应用层不会使用 但是内核控制函数是理解FreeRTOS中断的基础 接下来我们逐一分析这些内核函数 taskYIELD 该函数的作用是进行任务切换 这是一
  • ZYNQ中FreeRTOS中使用定时器

    使用普通的Timer中断方式时 Timer中断可以正常运行 但是UDP通信进程无法启动 其中TimerIntrHandler是中断服务程序 打印程序运行时间与从BRAM中读取的数据 void SetupInterruptSystem XSc
  • FreeRTOS打印任务对CPU的占有率

    1 配置RTOS 1 打开RTOS Config Parameter 找到Run Time And Task States gathering related definitions 使能GENERATE RUN TIME STATS US
  • FreeRTOS临界段和开关中断

    http blog sina com cn s blog 98ee3a930102wg5u html 本章教程为大家讲解两个重要的概念 FreeRTOS的临界段和开关中断 本章教程配套的例子含Cortex M3内核的STM32F103和Co
  • FreeRTOS学习(三)开关中断

    声明及感谢 跟随正点原子资料学习 在此作为学习的记录和总结 环境 keil stm32f103 背景知识 Cotex M3的NVIC最多支持240个IRQ 中断请求 1个不可屏蔽 NMI 1个Systick 滴答定时器 Cortex M处理
  • 基于STM32的FreeRTOS学习之中断测试实验(五)

    记录一下 方便以后翻阅 本章内容是接着上一章节进行的实际演练 1 实验目的 FreeRTOS可以屏蔽优先级低于configMAX SYSCALL INTERRUPT PRIORITY的中断 不会屏蔽高于其的中断 本次实验就是验证这个说法 本
  • FreeRTOS笔记(十)中断

    中断 当CPU在执行某一事件A时 发生另外一个更重要紧急的事件B请求CPU去处理 产生了中断 于是CPU暂时中断当前正在执行的事件A任务而对对事件B进行处理 CPU处理完事件B后再返回之前中断的位置继续执行原来的事件A 这一过程统称为中断
  • FreeRTOS死机原因

    1 中断回调函数中没有使用中断级API xxFromISR 函数 xSemaphoreGiveFromISR uart busy HighterTask 正确 xSemaphoreGive uart busy 错误 2 比configMAX
  • FreeRTOS学习---“定时器”篇

    总目录 FreeRTOS学习 任务 篇 FreeRTOS学习 消息队列 篇 FreeRTOS学习 信号量 篇 FreeRTOS学习 事件组 篇 FreeRTOS学习 定时器 篇 FreeRTOS提供了一种软件定时器 用来快速实现一些周期性的
  • freeRTOS出现任务卡死的情况。

    最近在做一个产品二代升级的项目 代码是上一任工程师留下的 很多BUG 而且融合了HAL库和LL库 以及github上下载的GSM源码 很不好用 我这边是将2G模块换成了4G 且添加了单独的BLE模块 因此只在源码的基础上 去除2G和BLE代
  • FreeRTOS轻量级同步--任务通知

    1 简介 在FreeRTOS的配置参数中的configUSE TASK NOTIFICATIONS宏打开 一般RTOS会默认打开 如图1所示 图1 notify宏开关 RTOS在创建任务时 会创建一个32位的通知值ulNotifiedVal
  • FreeRTOS实时操作系统(三)任务挂起与恢复

    系列文章 FreeRTOS实时操作系统 一 RTOS的基本概念 FreeRTOS实时操作系统 二 任务创建与任务删除 HAL库 FreeRTOS实时操作系统 三 任务挂起与恢复 FreeRTOS实时操作系统 四 中断任务管理 FreeRTO
  • FreeRTOS多任务调度器基础

    Cortex M4中SysTick调度器核心 Cortex M4中的中断管理 Cortex M4中影子栈指针 Cortex M4中SVC和PendSV异常 1 Cortex M4中SysTick调度器核心 systick每一次中断都会触发内
  • 使用 GCC 编译器的 ARM 内核的堆栈回溯(当存在 MSP 到 PSP 切换时)

    核心 ARM Cortex M4 编译器 GCC 5 3 0 ARM EABI 操作系统 免费 RTOS 我正在使用 gcc 库函数 Unwind Reason Code Unwind Backtrace Unwind Trace Fn v
  • 防止GCC LTO删除函数

    我使用 GCC ARM Embedded 和 FreeRTOS FreeRTOS具有的功能vTaskSwitchContext 仅在某些情况下使用 内联汇编代码 问题是 当我使用LTO时 GCC不考虑内联汇编代码并认为该函数没有被使用 因此
  • FreeRTOS 匈牙利表示法 [重复]

    这个问题在这里已经有答案了 我是 RTOS 和 C 编程的新手 而且我仍在习惯 C 的良好实践 因此 我打开了一个使用 FreeRTOS 的项目 我注意到操作系统文件使用匈牙利表示法 我知道一点符号 但面临一些新的 标准 FreeRTOS
  • 有可用的 FreeRTOS 解释语言库吗?

    我在一家公司工作 该公司使用 FreeRTOS 为多个设备创建固件 最近 我们对新功能的要求已经超出了我们固件工程师的工作能力 但我们现在也无力雇用任何新人 即使进行微小的更改 也需要固件人员在非常低的级别上进行修改 我一直在为 FreeR
  • C++ freeRTOS任务,非静态成员函数的无效使用

    哪里有问题 void MyClass task void pvParameter while 1 this gt update void MyClass startTask xTaskCreate this gt task Task 204
  • 有关 CMake 错误的问题:没有为目标提供源

    我正在尝试使用 cmake 和 eclipse 将 FreeRtos 添加到我的项目中 但出现错误 我运行的是 debian 10 我的 cmake 版本是 3 13 4 cmake 的文件可以在以下位置找到这个 git 仓库 https

随机推荐

  • Android广播发送机制剖析【android广播系列二】

    上篇博客大致说了说广播的注册机制 xff0c 动态注册和静态注册广播的原理还不一样 xff0c 动态广播最后HashMap中了 xff0c 最后放到mReceiverResolver中 xff0c 以后当ActivityManagerSer
  • 观察者模式--Java设计模式

    观察者模式定义 xff1a 定义了对象之间的一对多的依赖 xff0c 这样一来 xff0c 当一个对象发生改变状态的时候 xff0c 它的所有依赖者都会收到通知并自动更新 参考如下图 xff1a 观察者设计模式也叫发布 订阅模式 也可以称作
  • Android——RuntimePermission介绍

    1 介绍 androidM版本上 xff0c 对permission的管理做了部分改动 xff0c 针对dangerous permission xff0c 不在安装的时候给予权限 xff0c 而是在运行过程中咨询用户是否给予app响应的权
  • Android中launcherMode="singleTask"详解【android源码解析六】

    android中launcherMode有4中属性 xff1a standard 默认 xff0c singleTop xff0c singleTask和 singleInstance xff1b 网上有好多例子讲解这四种关系的 xff1a
  • Android闹钟最终版【android源码闹钟解析】

    我以前写了个复杂闹钟的demo xff0c 参见 Android闹钟 复杂版 大明进化十五 但是里面的bug有一些 xff0c 好多人留言 xff0c 所以我就看看源码 xff0c 找找原因 xff1f 顺便把源码代码整理出来 xff0c
  • Smali--Dalvik虚拟机指令语言-->【android_smali语法学习一】

    最近一周在研究rom移植 xff0c 所以就对Smali语言学习了一下 xff0c Smali语言其实就是Davlik的寄存器语言 xff1b Smali语言就是android的应用程序 apk通过apktool反编译出来的都有一个smal
  • 程序员平均月薪过万,想当程序员的话,大学学那些专业会更好呢?

    在互联网时代 xff0c 程序员成为炙手可热的职业 虽然加班累成狗 xff0c 也有可能面临英年早秃的局面 xff0c 但是不得不说程序员的工资高于很多很多传统行业职位的工资 据统计 xff0c 应届程序员毕业生在一线城市平均月薪达到8k
  • android4.0自定义锁屏总结【android锁屏研究一】

    最近搬家了 xff0c 从北京 gt 深圳 xff0c 除了天气有点不同外 xff0c 其他的都差不多 xff0c 工作性质和以前也类似 xff01 纪念一下自己的迁移 题外话 转载请表明出处 xff1a http blog csdn ne
  • android系统锁屏详解【android锁屏解析二】

    谷歌的代码写的确实不错 xff0c 我很幸运 xff0c 一开始接触代码就赶上了谷歌这个开源的系统 xff0c 让我的视野开阔了很多 xff0c 也让我看到了优秀的代码工程师写到的代码 心怀感恩之心 题记 我的有篇文章说了这个锁屏 xff0
  • 单片机学习笔记8--按键和外部中断(基于百问网STM32F103系列教程)

    第八章 按键和外部中断 第一节 按键原理 GPIO内部上拉较弱 xff0c 可根据满足电路需求选择是用内部的上拉还是自己外接上拉 根据原理图可知 xff0c 当按键松开时 xff0c 单片机引脚连接在高电平上 xff0c GPIO口输入高电
  • ARM架构与编程6--重定位(基于百问网ARM架构与编程教程视频)

    一 启动程序流程 我们之前讲过 xff0c 单片机有根据boot的不同 xff0c 有三种启动方式 xff1a boot0boot1启动模式0Xflash启动10系统存储器11内置SRAM 单片机上电复位后 xff0c 运行main函数 以
  • LVGL8.1版本笔记

    之前学过LVGL6 xff0c 现在版本更新到LVGL8了 xff0c 学习下新版本 xff0c 本文主要是记录一些LVGL8的新特性 xff0c 区别和lvgl6的不同之处 lv task handler 的作用 链接 lv task h
  • STM32F407定时器输入捕获

    STM32F407定时器一共有14个 其中分为基本定时器 通用定时器 高级定时器 具体功能如上图所示 下面介绍下输入捕获模式 输入捕获 xff1a 通过检测TIMx CHx上的边沿信号 在边沿信号发生跳变的时候 将当前的定时器的值 TIMx
  • 几种常见的存储器

    存储器分为易失性存储器和非易失性存储器 其中的易失性是指存储器掉电是否能保存数据 易失性存储器 xff1a 就是我们常说的RAM xff0c 这种存储器掉电后所有数据都消失 xff0c 不会被保存 xff0c 重新上电之后 xff0c 原来
  • FreeRTOS初级篇----优先级实验

    FreeRTOS创建任务时 xff0c 可以设置任务的优先级 在FreeRTOS中优先级的值越小 xff0c 优先级越低 xff0c 其他的RTOS都是优先级值越小 xff0c 优先级越高 xff0c 这点需要注意 任务优先级实验 xff1
  • FreeRTOS初级篇----删除任务

    任务既然可以被创建 xff0c 那么自然也可以被删除 xff0c 对于某些执行一次或几次就不需要的任务来说 xff0c 可以在执行完成后 xff0c 把此任务删除 xff0c 可以通过别的任务将其删除 xff0c 也可以任务自己删除自己 任
  • FreeRTOS初级篇----一个函数创建多个任务

    之前说过 xff0c 创建任务的时候 xff0c 可以传递给任务参数 xff0c 下面通过实验 xff0c 对一个任务函数传入不同的参数展示如何使用一个函数创建多个任务 任务函数 xff1a 把传入的数据转换为int型数据然后打印出来 sp
  • 大学生想要通过看书自学编程,却始终没成功,是因为你没有技巧!

    在大学里面 xff0c 有很多的方式来学习编程 xff0c 从在线教程到完成在线课程 xff0c 到密集编程的新手训练营 教程都很棒 xff0c 但它们通常只给你基础知识 与此同时 xff0c 在线教程和线下课程可能非常昂贵 xff0c 虽
  • FreeRTOS初级篇----任务管理

    一 任务状态 再FreeRTOS中 xff0c CPU同一时刻只执行一个任务 xff0c 只不过是所有任务切换的速度特别快 xff0c 默认1ms切换一次任务 xff0c 所以宏观上来看就是CPU再同时运行所有任务 根据任务的执行情况 xf
  • FreeRTOS中级篇

    一 内存管理 1 五种内存管理模式 FreeRTOS提供了5种内存管理实现方法 xff0c 可以根据不同的使用场合选择不同的模式 关于5中内存管理模式可以看下面这篇文章 链接 https zhuanlan zhihu com p 11527