单片机内存管理剖析

一、概述

在单片机系统中,内存资源通常是有限的,因此高效的内存管理至关重要。合理地分配和使用内存可以提高系统的性能和稳定性,避免内存泄漏和碎片化问题。单片机的内存主要包括程序存储器(如 Flash)和数据存储器(如 RAM),其中数据存储器又可进一步分为静态数据区、栈区和堆区。动态内存分配主要发生在堆区,而 sbrkmallocfree 这三个函数 在堆内存管理中起着关键作用。

二、sbrk:底层的内存边界调整

2.1 原理

sbrk 是一个底层的系统调用(在某些单片机库中也有对应的实现),其核心功能是调整进程数据段的结束地址,也就是 break 指针。通过改变 break 指针的位置,可以实现堆内存的扩展和收缩。当传入一个正的增量值时,break 指针向后移动,堆内存得到扩展;当传入一个负的增量值时,break 指针向前移动,堆内存被收缩。

2.2 源码示例与解释

#include <stdint.h>
#include <errno.h>// 假设这是链接脚本定义的堆起始和结束地址
extern char _end[];
extern char _heap_end[];
// 当前堆指针
static char *curbrk = _end;
// sbrk 函数实现
void *_sbrk(int incr) {char *old_brk = curbrk;char *new_brk = curbrk + incr;// 边界检查if (new_brk < _end || new_brk > _heap_end) {errno = ENOMEM;  // 设置错误号表示内存不足return (void *)-1;}curbrk = new_brk;return (void *)old_brk;
}
  • 全局变量
    • _end:由链接脚本确定,代表堆的起始地址。
    • _heap_end:同样由链接脚本确定,代表堆的最大可用地址。
    • curbrk:静态变量,记录当前堆的结束地址,初始化为 _end
  • 函数逻辑
    1. 保存当前的 curbrkold_brk 中,这将作为函数的返回值。
    2. 根据传入的 incr 计算新的堆结束地址 new_brk
    3. 进行边界检查,确保 new_brk 在合法范围内(不小于 _end 且不大于 _heap_end)。如果超出范围,设置 errnoENOMEM 并返回 (void *)-1 表示内存分配失败。
    4. 如果边界检查通过,更新 curbrknew_brk,并返回 old_brk,它指向新分配内存的起始位置。

2.3 使用场景和注意事项

  • 使用场景sbrk 通常作为底层的内存分配原语,为更高级的内存分配函数(如 malloc)提供支持。在一些简单的单片机应用中,如果只需要简单的内存扩展和收缩操作,也可以直接使用 sbrk
  • 注意事项
    • 由于 sbrk 直接操作堆的边界,使用不当可能会导致内存越界访问,破坏其他重要的数据。
    • sbrk 分配的内存是连续的,频繁的扩展和收缩操作可能会导致内存碎片化,降低内存的利用率。

三、malloc:用户级的动态内存分配

3.1 原理

malloc 是 C 标准库中提供的用于动态内存分配的函数,它建立在 sbrk 的基础之上。malloc 函数的主要任务是根据用户请求的内存大小,在堆中找到合适的空闲内存块并返回其起始地址。为了管理堆中的空闲内存,malloc 通常会维护一个空闲块链表,使用不同的分配策略(如首次适配、最佳适配等)来查找合适的空闲块。

3.2 源码示例与解释

#include <stdio.h>
#include <stdint.h>
#include <errno.h>
// 内存块结构体
typedef struct mem_block {size_t size;int is_free;struct mem_block *next;
} MemBlock;// 空闲链表头指针
static MemBlock *free_list = NULL;// 分配内存
void *malloc(size_t size) {MemBlock *current = free_list;MemBlock *prev = NULL;// 查找合适的空闲块while (current != NULL) {if (current->is_free && current->size >= size) {current->is_free = 0;// 如果空闲块比需求大,分割空闲块if (current->size > size + sizeof(MemBlock)) {MemBlock *new_free_block = (MemBlock *)((char *)current + sizeof(MemBlock) + size);new_free_block->size = current->size - size - sizeof(MemBlock);new_free_block->is_free = 1;new_free_block->next = current->next;current->size = size;current->next = new_free_block;}return (void *)(current + 1);}prev = current;current = current->next;}// 没有合适的空闲块,调用 sbrk 扩展堆size_t total_size = size + sizeof(MemBlock);MemBlock *new_block = (MemBlock *)_sbrk(total_size);if (new_block == (MemBlock *)-1) {return NULL;}new_block->size = size;new_block->is_free = 0;new_block->next = NULL;if (prev != NULL) {prev->next = new_block;} else {free_list = new_block;}return (void *)(new_block + 1);
}
  • 数据结构

    • MemBlock
      

      结构体:用于表示堆中的内存块,包含三个成员:

      • size:记录内存块的大小。
      • is_free:标记该内存块是否空闲。
      • next:指向下一个内存块的指针,用于构建空闲块链表。
    • free_list:指向空闲块链表的头指针,初始化为 NULL

  • 函数逻辑

    1. 查找空闲块:遍历空闲块链表 free_list,使用首次适配策略查找第一个大小足够的空闲块。
    2. 分割空闲块:如果找到的空闲块比请求的大小大,将其分割为两部分:一部分用于满足当前请求,另一部分作为新的空闲块插入到链表中。
    3. 扩展堆:如果在空闲块链表中没有找到合适的空闲块,调用 sbrk 函数扩展堆空间,分配一块新的内存,并将其初始化为一个新的内存块。
    4. 返回内存地址:返回分配的内存块的起始地址(跳过 MemBlock 结构体部分)。

在这里插入图片描述

四、free:动态内存的释放

4.1 原理

free 函数用于释放 malloccallocrealloc 分配的内存块。当调用 free 时,它会将指定的内存块标记为空闲,并尝试合并相邻的空闲块,以减少内存碎片化。

4.2 源码示例与解释

// 释放内存
void free(void *ptr) {if (ptr == NULL) return;// 获取内存块头部MemBlock *block = (MemBlock *)ptr - 1;block->is_free = 1;// 合并相邻的空闲块MemBlock *current = free_list;MemBlock *prev = NULL;// 找到合适的插入位置while (current != NULL && current < block) {prev = current;current = current->next;}// 合并前一个空闲块if (prev != NULL && prev->is_free) {prev->size += block->size + sizeof(MemBlock);prev->next = block->next;block = prev;}// 合并后一个空闲块if (current != NULL && current->is_free) {block->size += current->size + sizeof(MemBlock);block->next = current->next;}// 如果没有前一个块,更新空闲链表头if (prev == NULL) {free_list = block;} else {prev->next = block;}block->next = current;
}
  • 函数逻辑
    1. 空指针检查:如果传入的指针 ptrNULL,直接返回,不进行任何操作。
    2. 标记为空闲:通过指针计算得到内存块的头部信息(MemBlock 结构体),将其 is_free 标记设置为 1,表示该内存块已空闲。
    3. 合并相邻空闲块
      • 遍历空闲块链表,找到合适的位置插入该空闲块。
      • 检查前一个和后一个内存块是否空闲,如果是,则将它们合并成一个更大的空闲块。
    4. 更新空闲链表:根据合并结果更新空闲块链表的指针,确保链表的正确性。

4.3 使用场景和注意事项

  • 使用场景:在不再需要使用动态分配的内存时,必须调用 free 函数释放内存,以避免内存泄漏。
  • 注意事项
    • 只能释放由 malloccallocrealloc 分配的内存,释放其他内存可能会导致未定义行为。
    • 不要多次释放同一块内存,这会导致双重释放错误,可能会破坏内存管理数据结构。

在这里插入图片描述

sbrkmallocfree 是单片机内存管理中重要的工具,它们相互协作,实现了堆内存的动态分配和释放。sbrk 作为底层的系统调用,提供了基本的内存扩展和收缩功能;malloc 基于 sbrk 实现了用户级的动态内存分配接口,方便程序员在运行时分配所需的内存;free 则负责释放不再使用的内存,避免内存泄漏和碎片化。在实际应用中,需要合理使用这些函数,注意内存的分配和释放规则,以确保系统的稳定性和性能。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

“AI质量评估系统:智能守护,让品质无忧

嘿&#xff0c;各位小伙伴们&#xff01;今天咱们来聊聊一个在现代社会中越来越重要的角色——AI质量评估系统。你知道吗&#xff1f;在这个快速发展的时代&#xff0c;产品质量已经成为企业生存和发展的关键。而AI质量评估系统&#xff0c;就像是我们的智能守护神&#xff0c;…

人工智能:从基础到前沿

目录 目录 1. 引言 2. 人工智能基础 2.1 什么是人工智能&#xff1f; 2.2 人工智能的历史 2.3 人工智能的分类 3. 机器学习 3.1 机器学习概述 3.2 监督学习 3.3 无监督学习 3.4 强化学习 4. 深度学习 4.1 深度学习概述 4.2 神经网络基础 4.3 卷积神经网络&#…

Centos7系统php8编译安装ImageMagick/Imagick扩展教程整理

Centos7系统php8编译安装ImageMagick/Imagick扩展教程整理 安装php8安装ImageMagick1、下载ImageMagick2、解压并安装3、查看是否安装成功 安装imagick扩展包 安装php8 点我安装php8 安装ImageMagick 1、下载ImageMagick wget https://www.imagemagick.org/download/ImageMa…

基于微信阅读网站小程序的设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

51单片机入门_02_C语言基础0102

C语言基础部分可以参考我之前写的专栏C语言基础入门48篇 以及《从入门到就业C全栈班》中的C语言部分&#xff0c;本篇将会结合51单片机讲差异部分。 课程主要按照以下目录进行介绍。 文章目录 1. 进制转换2. C语言简介3. C语言中基本数据类型4. 标识符与关键字5. 变量与常量6.…

【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】1.2 ndarray解剖课:多维数组的底层实现

1.2 《ndarray解剖课&#xff1a;多维数组的底层实现》 内容介绍 NumPy 的 ndarray 是其核心数据结构&#xff0c;用于高效处理多维数组。在这篇文章中&#xff0c;我们将深入解析 ndarray 的底层实现&#xff0c;探讨其内存结构、维度、数据类型、步长等关键概念&#xff0c…

C++——list的了解和使用

目录 引言 forward_list与list 标准库中的list 一、list的常用接口 1.list的迭代器 2.list的初始化 3.list的容量操作 4.list的访问操作 5.list的修改操作 6.list的其他操作 二、list与vector的对比 结束语 引言 本篇博客要介绍的是STL中的list。 求点赞收藏评论…

mysql 学习6 DQL语句,对数据库中的表进行 查询 操作

前期准备数据 重新create 一张表 create table emp(id int comment 编号,workno varchar(10) comment 工号,name varchar(10) comment 姓名,gender char comment 性别,ager tinyint unsigned comment 年龄,idcard char(18) comment 身份证号,workaddress varchar(10) c…

Arduino大师练成手册 -- 控制 MH-SD 卡模块

要在 Arduino 上控制 MH-SD 卡模块&#xff0c;你可以按照以下步骤进行&#xff1a; 硬件连接 VCC&#xff1a;连接到 Arduino 的 3.3V 或 5V 引脚&#xff08;根据模块的要求&#xff09;。 GND&#xff1a;连接到 Arduino 的 GND 引脚。 CS&#xff1a;连接到 Arduino 的…

【记录】日常|从零散记录到博客之星Top300的成长之路

文章目录 shandianchengzi 2024 年度盘点概述写作风格简介2024年的创作内容总结 shandianchengzi 2024 年度盘点 概述 2024年及2025年至今我创作了786即84篇文章&#xff0c;加上这篇就是85篇。 很荣幸这次居然能够入选博客之星Top300&#xff0c;这个排名在我之前的所有年份…

Elastic Agent 对 Kafka 的新输出:数据收集和流式传输的无限可能性

作者&#xff1a;来 Elastic Valerio Arvizzigno, Geetha Anne 及 Jeremy Hogan 介绍 Elastic Agent 的新功能&#xff1a;原生输出到 Kafka。借助这一最新功能&#xff0c;Elastic 用户现在可以轻松地将数据路由到 Kafka 集群&#xff0c;从而实现数据流和处理中无与伦比的可扩…

对神经网络基础的理解

目录 一、《python神经网络编程》 二、一些粗浅的认识 1&#xff09; 神经网络也是一种拟合 2&#xff09;神经网络不是真的大脑 3&#xff09;网络构建需要反复迭代 三、数字图像识别的实现思路 1&#xff09;建立一个神经网络类 2&#xff09;权重更新的具体实现 3&am…

Java后端之AOP

AOP&#xff1a;面向切面编程&#xff0c;本质是面向特定方法编程 引入依赖&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>示例&#xff1a;记录…

【25考研】人大计算机考研复试该怎么准备?有哪些注意事项?

人大毕竟是老牌985&#xff0c;复试难度不会太低&#xff01;建议同学认真复习&#xff01;没有机试还是轻松一些的&#xff01; 一、复试内容 由公告可见&#xff0c;复试包含笔试及面试&#xff0c;没有机试&#xff01; 二、参考书目 官方无给出参考书目&#xff0c;可参照…

汽车OEMs一般出于什么目的来自定义Autosar CP一些内容

汽车OEMs在使用AUTOSAR CP(Classic Platform)协议时,可能会根据自身的特定需求对标准协议进行修改,形成自己的企业标准(企标)。这种修改通常是为了满足特定的硬件平台、功能需求、安全要求或优化性能。以下是一些常见的修改场景和例子: 1. 硬件平台适配 企业可能会根据…

基于语义-拓扑-度量表征引导的大语言模型推理的空中视觉语言导航

1. 摘要翻译及主要贡献点 摘要&#xff1a; 空中视觉语言导航&#xff08;VLN&#xff09;是一项新兴任务&#xff0c;它使无人机能够通过自然语言指令和视觉线索在户外环境中导航。由于户外空中场景中复杂的空间关系&#xff0c;这项任务仍然具有挑战性。本文提出了一种端到…

HTML-新浪新闻-实现标题-样式1

用css进行样式控制 css引入方式&#xff1a; --行内样式&#xff1a;写在标签的style属性中&#xff08;不推荐&#xff09; --内嵌样式&#xff1a;写在style标签中&#xff08;可以写在页面任何位置&#xff0c;但通常约定写在head标签中&#xff09; --外联样式&#xf…

LongLoRA:高效扩展大语言模型上下文长度的微调方法

论文地址&#xff1a;https://arxiv.org/abs/2309.12307 github地址&#xff1a;https://github.com/dvlab-research/LongLoRA 1. 背景与挑战 大语言模型&#xff08;LLMs&#xff09;通常在预定义的上下文长度下进行训练&#xff0c;例如 LLaMA 的 2048 个 token 和 Llama2 的…

.NET9增强OpenAPI规范,不再内置swagger

ASP.NETCore in .NET 9.0 OpenAPI官方文档ASP.NET Core API 应用中的 OpenAPI 支持概述 | Microsoft Learnhttps://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/openapi/overview?viewaspnetcore-9.0https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/ope…

景联文科技加入AIIA联盟数据标注分委会

2025年1月16日&#xff0c;中国人工智能产业发展联盟&#xff08;简称AIIA&#xff09;数据委员会数据标注分委会&#xff08;以下简称“分委会”&#xff09;正式成立。景联文科技成为第一批AIIA联盟数据标注分委会委员单位。 数据标注分委会的成立旨在搭建数据标注领域产学研…