单片机内存管理剖析

一、概述

在单片机系统中,内存资源通常是有限的,因此高效的内存管理至关重要。合理地分配和使用内存可以提高系统的性能和稳定性,避免内存泄漏和碎片化问题。单片机的内存主要包括程序存储器(如 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.mzph.cn/diannao/68078.shtml

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

相关文章

1. 握手问题python解法——2024年省赛蓝桥杯真题

原题传送门&#xff1a;1.握手问题 - 蓝桥云课 问题描述 小蓝组织了一场算法交流会议&#xff0c;总共有 50人参加了本次会议。在会议上&#xff0c;大家进行了握手交流。按照惯例他们每个人都要与除自己以外的其他所有人进行一次握手 (且仅有一次)。但有 7 个人&#xff0c;…

15.7k!DISM++一款快捷的系统优化工具

软件介绍 链接 软件介绍 dism是一款由初雨团队开发的win系统优化工具&#xff0c;可当作是微软系统命令行工具dism的GUI版本。可用作系统垃圾清除、启动项管理、程序卸载、驱动管理、系统优化等 该工具个人感觉最重要的就是系统优化选项&#xff0c;它将一些实用、无用或者没…

idea修改模块名导致程序编译出错

本文简单描述分别用Idea菜单、pom.xml文件管理项目模块module 踩过的坑&#xff1a; 通过idea菜单创建模块&#xff0c;并用idea菜单修改模块名&#xff0c;结构程序编译报错&#xff0c;出错的代码莫名奇妙。双击maven弹窗clean时&#xff0c;还是报错。因为模块是新建的&am…

微服务学习-Nacos 注册中心实战

1. 注册中心的设计思路 1.1. 微服务为什么会用到注册中心&#xff1f; 服务与服务之间调用需要有服务发现功能&#xff1b;例如订单服务调用库存服务&#xff0c;库存服务如果有多个&#xff0c;订单服务到底调用那个库存服务呢&#xff08;负载均衡器&#xff09;&#xff0…

vim如何设置自动缩进

:set autoindent 设置自动缩进 :set noautoindent 取消自动缩进 &#xff08;vim如何使设置自动缩进永久生效&#xff1a;vim如何使相关设置永久生效-CSDN博客&#xff09;

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

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

ELK环境搭建

文章目录 1.ElasticSearch安装1.安装的版本选择1.SpringBoot版本&#xff1a;2.4.2 找到依赖的spring-data-elasticsearch的版本2.spring-data-elasticsearch版本&#xff1a;4.1.3 找到依赖的elasticsearch版本3.elasticsearch版本&#xff1a;7.9.3 2.安装1.官方文档2.下载压…

Ubuntu20.04 运行 PL-VIO

文章目录 运行后不知为何没有线特征 运行后不知为何没有线特征

C#与AI的共同发展

C#与人工智能(AI)的共同发展反映了编程语言随着技术进步而演变&#xff0c;以适应新的挑战和需要。自2000年微软推出C#以来&#xff0c;这门语言经历了多次迭代&#xff0c;不仅成为了.NET平台的主要编程语言之一&#xff0c;还逐渐成为构建各种类型应用程序的强大工具。随着时…

什么情况该换手机?先看后买不踩坑

现在的智能手机发展的非常快&#xff0c;很多刚出来的1000多元的手机性能已经可以流畅玩游戏、刷视频了&#xff0c;而且基本上也能使用3-5年的时。如果真要把手机用到实在不能用了&#xff0c;可能真的会影响生活体验&#xff0c;还有可能因为电池鼓包等问题发生危险&#xff…

centos操作系统上以service形式运行blackbox_exporter监控网页端口

文章目录 前言一、blackbox_exporter是什么二、使用步骤1.获取二进制文件2.准备部署脚本3.执行命令&#xff0c;进行部署4.prometheus中增加需要监控页面的job信息 三、查看部署结果四、配置到grafana中总结 前言 记录一下centos操作系统上以简单的service形式运行blackbox_ex…

使用github提交Pull Request的完整流程

文章目录 1.Fork仓库2. git clone 仓库在本地3.对项目进行修改开发4.上传项目到远程仓库操作补充1. git add .2. git commit -m "提交信息"3. git pull4. git push总结完整工作流程示例 5.将更新的项目pull Request给原来的仓库主人 当多人进行项目的开发的时候&…

python编写Socket程序

文章目录 编写非阻塞的TCP连接程序编写UDP的socket程序创建连接发送数据 多线程管理udp 编写非阻塞的TCP连接程序 下面代码使用了select模块来管理多个 socket 连接&#xff0c;server_socket.setblocking(0)将服务器 socket 设置为非阻塞模式 &#xff0c;在接收数据时&#…

PHP礼品兑换系统小程序

&#x1f381; 礼品兑换系统&#xff1a;革新企业礼品管理&#xff0c;专属神器来袭&#xff01; &#x1f4bb; 一款专为追求高效与个性化的现代企业量身打造的礼品兑换系统&#xff0c;它基于强大的ThinkPHP框架与前沿的Uniapp技术栈深度融合&#xff0c;不仅完美适配礼品卡…

mapbox加载geojson,鼠标移入改变颜色,设置样式以及vue中的使用

全国地图json数据下载地址 目录 html加载全部代码 方式一&#xff1a;使用html方式加载geojson 1. 初始化地图 2. 加载geojson数据 设置geojson图层样式&#xff0c;设置type加载数据类型 设置线条 鼠标移入改变颜色&#xff0c;设置图层属性&#xff0c;此处是fill-extru…

Langchain+讯飞星火大模型Spark Max调用

1、安装langchain #安装langchain环境 pip install langchain0.3.3 openai -i https://mirrors.aliyun.com/pypi/simple #灵积模型服务 pip install dashscope -i https://mirrors.aliyun.com/pypi/simple #安装第三方集成,就是各种大语言模型 pip install langchain-comm…

【kong gateway】5分钟快速上手kong gateway

kong gateway的请求响应示意图 安装 下载对应的docker 镜像 可以直接使用docker pull命令拉取&#xff0c;也可以从以下地址下载&#xff1a;kong gateway 3.9.0.0 docker 镜像 https://download.csdn.net/download/zhangshenglu1/90307400&#xff0c; postgres-13.tar http…

高效查找:二分查找算法解析

1.二分查找简介 二分查找算法&#xff08;Binary Search&#xff09;是一种高效的查找算法&#xff0c;适用于有序数组或序列。它的基本思想是通过逐步缩小查找范围&#xff0c;将查找区间一分为二&#xff0c;直到找到目标值或确定目标值不存在。 算法原理&#xff1a;在数组…

数据统计–图形报表(day11)

Apache ECharts 介绍 Apache ECharts 介绍 Apache ECharts 是一款基于 Javascript 的数据可视化图表库&#xff0c;提供直观&#xff0c;生动&#xff0c;可交互&#xff0c;可个性化定制的数据可视化图表。 官网地址&#xff1a;Apache ECharts 入门案例 Apache Echarts官方…

Docker可视化管理工具Portainer

Portainer简介 Portainer 是一个轻量级的、开源的容器管理工具&#xff0c;提供了一个直观的 Web 用户界面&#xff08;UI&#xff09;&#xff0c;用于管理 Docker 和 Kubernetes 环境。它简化了容器的部署、监控和管理&#xff0c;特别适合不熟悉命令行操作的用户或团队。 …