RT-Thread 内存管理(学习二)

news2024/11/24 11:10:09

内存堆管理应用示例

这是一个内存堆的应用示例,这个程序会创建一个动态的线程,这个线程会动态申请内存并释放,每次申请更大的内存,当申请不到的时候就结束。

#include <rtthread.h>

#define THREAD_PRIORITY      25
#define THREAD_STACK_SIZE    512
#define THREAD_TIMESLICE     5

/* 线程入口 */
void thread1_entry(void *parameter)
{
    int i;
    char *ptr = RT_NULL; /* 内存块的指针 */

    for (i = 0; ; i++)
    {
        /* 每次分配 (1 << i) 大小字节数的内存空间 */
        ptr = rt_malloc(1 << i);

        /* 如果分配成功 */
        if (ptr != RT_NULL)
        {
            rt_kprintf("get memory :%d byte\n", (1 << i));
            /* 释放内存块 */
            rt_free(ptr);
            rt_kprintf("free memory :%d byte\n", (1 << i));
            ptr = RT_NULL;
        }
        else
        {
            rt_kprintf("try to get %d byte memory failed!\n", (1 << i));
            return;
        }
    }
}

int dynmem_sample(void)
{
    rt_thread_t tid = RT_NULL;

    /* 创建线程 1 */
    tid = rt_thread_create("thread1",
                           thread1_entry, RT_NULL,
                           THREAD_STACK_SIZE,
                           THREAD_PRIORITY,
                           THREAD_TIMESLICE);
    if (tid != RT_NULL)
        rt_thread_startup(tid);

    return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(dynmem_sample, dynmem sample);

内存池

内存堆管理器可以分配任意大小的内存块,非常灵活和方便。
但其也存在明显的缺点:

  1. 分配效率不高,在每次分配时,都要在空闲内存块查找。
  2. 容易产生内存碎片。

为了提高内存分配的效率,并且避免内存碎片,RT-Thread提供了另外一种内存管理方法:内存池(Memory Pool)。

内存池是一种内存分配方式,用于分配大量大小相同的小内存块,它可以极大加快内存分配与释放的速度,且能尽量避免内存碎片化。
此外,RT-Thread的内存池支持线程挂起功能,当内存池中无空闲内存块时,申请线程会被挂起,直到内存池中有新的可用内存块,再将挂起的申请线程唤醒。

内存池的线程挂起功能非常适合需要通过内存资源进行同步的场景,例如播放音乐时,播放器线程会对音乐文件进行解码,然后发送到声卡驱动,从而驱动硬件播放音乐。

在这里插入图片描述

  1. 当播放器线程需要解码数据时,就会向内存池请求内存块,如果内存块已经用完,线程将被挂起,否则它将获得内存块以放置解码的数据。
  2. 播放器线程把包含解码数据的内存块写入到声卡抽象设备中(线程会立刻返回,继续解码出更多的数据)。
  3. 当声卡设备写入完成后,将调用播放器线程设置的回调函数,释放写入的内存块,如果在此之前,播放器线程因为把内存池里的内存块都用完而被挂起的话,那么这时它将被将唤醒,并继续进行解码。

内存池控制块

内存池控制块是操作系统用于管理内存池的一个数据结构,它会存放内存池的一些信息,例如内存块数据区域开始地址,内存块大小和内存块列表等,也包含内存块与内存块之间连接用的链表结构,因内存块不可用而挂起的线程等待事件集合等。

struct rt_mempool
{
	struct rt_object parent;
	void *start_address; //内存池数据区域开始地址
	rt_size_t size;  	//内存池数据区域大小
	rt_size_t block_size; //内存块大小
	rt_uint8_t *block_list; //内存块列表
	/* 内存池数据区域中能够容纳的最大内存块数  */
    rt_size_t     block_total_count;
    /* 内存池中空闲的内存块数  */
    rt_size_t     block_free_count;
    /* 因为内存块不可用而挂起的线程列表 */
    rt_list_t     suspend_thread;
    /* 因为内存块不可用而挂起的线程数 */
    rt_size_t     suspend_thread_count;
};

typedef struct rt_mempool* rt_mp_t;

内存块分配机制

内存池在创建时先向系统申请一大块内存,然后分成同样大小的多个小内存块,小内存块直接通过链表连接起来(此链表也称为空闲链表)。
每次分配的时候,从空闲链表中取出链头上第一个内存块,提供给申请者。
物理内存中允许存在多个大小不同的内存池,每一个内存池又由多个空闲内存块组成,内核用它们来进行内存管理。
当一个内存池对象被创建时,内存池对象就被分配给了一个内存池控制块,内存池控制块的参数包括内存池名,内存缓冲区,内存块大小,块数以及一个线程等待队列。

在这里插入图片描述
内核负责给内存池分配内存池控制块,它同时也接收用户线程的分配内存块申请。
内存池一旦初始化完成,内部的内存块大小将不能再做调整。

suspend_thread形成了一个申请线程等待列表,即当内存池中无可用内存块,并且申请线程允许等待时,申请线程将挂在suspend_thread链表上。

内存池的管理方式

内存池控制块是一个结构体,其中含有内存池相关的重要参数。
在这里插入图片描述

创建和删除内存池

创建内存池操作将会创建一个内存池对象并从堆上分配一个内存池。

rt_mp_t rt_mp_create(const char* name,rt_size_t block_count, rt_size_t block_size);

使用该函数接口可以创建一个与需求的内存块大小、数目相匹配的内存池,前提当然是在系统资源允许的情况下(最主要的内存堆内存资源)才能创建成功。

创建内存池时,需要给内存池指定一个名称。
然后内核从系统中申请一个内存池对象,然后从内存堆中分配一块由块数目和块大小计算得来的内存缓冲区,接着初始化内存池对象,并将申请成功的内存缓冲区组织成可用于分配的空闲块链表。

删除内存池将删除内存池对象并释放申请的内存。

rt_err_t rt_mp_delete(rt_mp_t mp);

删除内存池时,会首先唤醒等待在该内存池对象上的所有线程(返回-RT_ERROR),然后再释放已从内存堆上分配的内存池数据存放区域,然后删除内存池对象。

内存池应用示例

这是一个静态内存池应用例程,创建一个静态的内存池对象,2个动态线程,1个线程试图从内存池中获得内存块,另一个线程释放内存块。

#include <rtthread.h>

static rt_uint8_t *ptr[50];
static rt_uint8_t mempool[4096];
static struct rt_mempool mp;

#define THREAD_PRIORITY      25
#define THREAD_STACK_SIZE    512
#define THREAD_TIMESLICE     5

/* 指向线程控制块的指针 */
static rt_thread_t tid1 = RT_NULL;
static rt_thread_t tid2 = RT_NULL;

/* 线程 1 入口 */
static void thread1_mp_alloc(void *parameter)
{
    int i;
    for (i = 0 ; i < 50 ; i++)
    {
        if (ptr[i] == RT_NULL)
        {
            /* 试图申请内存块 50 次,当申请不到内存块时,
               线程 1 挂起,转至线程 2 运行 */
            ptr[i] = rt_mp_alloc(&mp, RT_WAITING_FOREVER);
            if (ptr[i] != RT_NULL)
                rt_kprintf("allocate No.%d\n", i);
        }
    }
}

/* 线程 2 入口,线程 2 的优先级比线程 1 低,应该线程 1 先获得执行。*/
static void thread2_mp_release(void *parameter)
{
    int i;

    rt_kprintf("thread2 try to release block\n");
    for (i = 0; i < 50 ; i++)
    {
        /* 释放所有分配成功的内存块 */
        if (ptr[i] != RT_NULL)
        {
            rt_kprintf("release block %d\n", i);
            rt_mp_free(ptr[i]);
            ptr[i] = RT_NULL;
        }
    }
}

int mempool_sample(void)
{
    int i;
    for (i = 0; i < 50; i ++) ptr[i] = RT_NULL;

    /* 初始化内存池对象 */
    rt_mp_init(&mp, "mp1", &mempool[0], sizeof(mempool), 80);

    /* 创建线程 1:申请内存池 */
    tid1 = rt_thread_create("thread1", thread1_mp_alloc, RT_NULL,
                            THREAD_STACK_SIZE,
                            THREAD_PRIORITY, THREAD_TIMESLICE);
    if (tid1 != RT_NULL)
        rt_thread_startup(tid1);


    /* 创建线程 2:释放内存池 */
    tid2 = rt_thread_create("thread2", thread2_mp_release, RT_NULL,
                            THREAD_STACK_SIZE,
                            THREAD_PRIORITY + 1, THREAD_TIMESLICE);
    if (tid2 != RT_NULL)
        rt_thread_startup(tid2);

    return 0;
}

/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(mempool_sample, mempool sample);

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信小程序通过createSelectorQuery获取元素 高度,宽度与界面距离

小程序官方有提供给我们一个 const query wx.createSelectorQuery() 函数 我们可以先编写这样一段代码 wxml <view><button bindtap"getDom">点击查看</button><view class "textIn" style "height: 100px;width: 30px;&quo…

IDEA 初始化配置

进入所有配置 配置外观 取消退出前确认和启动软件自动打开之前最后的项目 设置使用键盘Ctrl鼠标滚轮调整代码大小 设置字符集 设置构建工具 自动构建与自动编译 启用注解

think-rom(thinkphp) 数据库参数绑定bigint被处理成浮点数(科学计数)问题

背景 think-orm(db库)在处理长整型参数时会进行一次安全处理转换成浮点型&#xff0c;但php浮点数超过一定长度就会失去精准度变成科学计数&#xff0c;导致where条件匹配不上&#xff0c;这个问题目前覆盖thinkphp3到8全系&#xff1b; 解决方案 全局搜索 (float) $val[0] : …

迁移人大金仓问题汇总

迁移人大金仓问题汇总 1、SQL 错误 [42883]: 错误: 操作符不存在: tinyint Hint: 没有匹配指定名称和参数类型的操作符. 您也许需要增加明确的类型转换. SELECT id, model_id, process_definition_id, task_definition_key, TYPE, OPTIONS, scope_userid, scope_clas…

雷达分辨率单元、单向/双向雷达方程、天气雷达方程简介

一、点状目标 如果两个点状目标在一个分辨率单元中,经典脉冲雷达只能看到一个目标。 点状目标 二、雷达距离分辨率 对于简单的键控开/关脉冲调制: 对于使用脉冲内调制的雷达,距离分辨率取决于压缩脉冲的脉冲持续时间。脉冲压缩比(PCR)取决于传输带宽BWtx,即距离分辨率取…

大语言模型学到什么

背景&#xff1a; 这篇文章是对《LANGUAGE MODELS REPRESENT SPACE AND TIME》论文的翻译加解读。之所以选这篇文章是因为最近在研究大模型的可解释性&#xff0c;以及基于可解释性对大模型的下游任务适配做训练级别可控性增强研究。其实总结成两句话就是&#xff1a; 1.大模…

amber14自由能计算及增强采样方法

1分子力学简介 1.1分子力学的基本假设 1.2分子力学的主要形式 2分子力场 2.1分子力场的简介 2.2分子力场的原理 分子力场的分类及应用 二.LINUX入门 教学目标&#xff1a;掌握数值计算平台&#xff0c;熟悉计算机语言&#xff0c;能够使用vim编辑器简单编辑文件。 3LINUX 简介 …

84.柱状图中最大的矩形

class Solution { public:int largestRectangleArea(vector<int>& heights) {//法一&#xff1a;枚举宽&#xff0c;超时if(0){int nheights.size();int area0;for(int left0;left<n;left){int minHINT_MAX;for(int rightleft;right<n;right){minHmin(minH,hei…

Spring实例化源码解析之MessageSource(七)

前言 在阅读完 registerBeanPostProcessors 源码之后&#xff0c; 下一步就进入到 initMessageSource&#xff0c;这一步主要作用是初始化国际化文件。 源码分析 这段源码是一个Java方法&#xff0c;用于初始化消息源&#xff08;MessageSource&#xff09;。在Spring框架中…

万字详解HTTP协议面试必备技能

目录 一、HTTP 是什么 二、理解 "应用层协议" 2.1理解 HTTP 协议的工作过程 2.2HTTP 协议格式 2.3抓包工具的使用 2.4抓包工具的原理 2.5抓包结果 2.5.1HTTP请求 2.5.2HTTP响应 2.6协议格式总结 三、HTTP 请求 (Request) 3.1认识 URL 3.1.1URL 基本格式 …

Jmeter 多实例压测

Apache JMeter 是一个开源的 Java 应用程序&#xff0c;用于性能测试和负载测试。它最初是为测试 Web 应用程序而创建的&#xff0c;但现在已广泛用于测试各种不同类型的应用程序&#xff0c;包括数据库、消息队列、FTP 服务器等。JMeter 提供了丰富的功能&#xff0c;使您能够…

行内元素文字背景被截断的问题,如何进行修改?

1.问题如图所示 这里背景是被截断的&#xff0c;有什么css样式可以修改它呢&#xff1f; 效果实现如图&#xff1a; 不一样的地方在于这里的截断位置是引用了开头和结尾的位置。 如何实现&#xff1f; <!DOCTYPE html> <html lang"en"> <head><…

终于找到了!Jenkins最全最详细入门指南

目录 一、介绍 二、功能 三、搭建Jenkins目的 四、安装 五、Jenkins的三大概念 六、创建job & 配置git 七、构建触发器&#xff08;build triggers&#xff09; 八、运行python脚本 九、测试报告展示 十、配置邮件并发送 最后 一、介绍 Jenkins是开源的,使用Java编…

Linux系统部署PostgreSQL 单机数据库

安装方式 1 安装包方式 &#xff08;Packages and Installers&#xff09; 支持的操作系统包括 liunxMacosWindowsBSDSolaris 2 源码安装 &#xff08;Source code&#xff09; 下载源码包 通过下载地址PostgreSQL: File Browser 可以看到有各个版本的源码目录 选择13.1…

二维离散傅里叶变换的实现

二维离散傅里叶变换的实现 1.使用Python包实现2.使用c实现2.1 FFTW库安装2.2 结果比较 参考文献 1.使用Python包实现 import numpy as np import matplotlib.pyplot as plt anp.array([0, 2, 4, 1,6, 1, 3, 2,5]).reshape(3,3) fnp.fft.fft2(a) fshiftnp.fft.fftshift(f) mag…

CSS圆角大杀器,使用滤镜构建圆角及波浪效果

目录 1&#xff0c;border-radius实现圆角2&#xff0c;filter: contrast() 配合 filter: blur() 实现圆角3&#xff0c;使用 filter: contrast() 配合 filter: blur() 实现波浪效果 1&#xff0c;border-radius实现圆角 通常我们加个 border-radius就可实现一个div盒子的圆角…

分页查询(关键词: limit)

MySQL从小白到总裁完整教程目录:https://blog.csdn.net/weixin_67859959/article/details/129334507?spm1001.2014.3001.5502 比如现在有一张表emp有15条数据 我想每页只显示5条数据,分3页,这样看起来简捷一些,我该怎么实现呢 语法格式: select */列名 from 表名 limit 初…

CMAKE使用记录

CMAKE使用记录 CMake简化了针对同一项目的不同平台、不同编译器的构建过程和编译过程&#xff0c;能够管理各种规模的项目。 CMAKE命令记录 cmake命令说明 语法&#xff1a; # cmake构建项目的编译系统 Generate a Project Buildsystem > cmake [<options>] -B &…

85.最大矩形

单调栈&#xff0c;时间复杂度o(mn)&#xff0c;空间复杂度o(mn) class Solution { public:int maximalRectangle(vector<vector<char>>& matrix) {int mmatrix.size();if(m0){return 0;}int nmatrix[0].size();//记录矩阵中每个元素左边连续1的数量vector<…

如何开发一款高效便捷的搬家服务小程序

随着互联网的发展&#xff0c;小程序已成为各行各业重要的业务推广和用户服务平台。对于搬家行业而言&#xff0c;开发一款高效便捷的搬家服务小程序具有巨大的市场潜力。本文将为您详细介绍如何开发一款这样的搬家服务小程序。 一、进入乔拓云网后台 在开始制作搬家服务小程序…