当前位置:首页 > 公众号精选 > 程序喵大人
[导读]在使用C语言开发嵌入式产品的过程中,当使用到malloc函数时候都会有一个争议, “使用动态内存分配安全吗?” ,就连美国军方在safety-critical的嵌入式航空电子设备代码中,也禁止动态内存分配,我们来细细分析下。


在使用C语言开发嵌入式产品的过程中,当使用到malloc函数时候都会有一个争议, “使用动态内存分配安全吗?” ,就连美国军方在safety-critical的嵌入式航空电子设备代码中,也禁止动态内存分配,我们来细细分析下。

C 库函数 - malloc()

函数简介

malloc的全称是memory allocation,中文叫动态内存分配,用于申请一块连续的指定大小的内存块区域以void* 类型返回分配的内存区域地址。

当无法知道内存具体位置的时候,想要绑定真正的内存空间,就需要用到动态的分配内存,且分配的大小就是程序要求的大小。

函数的声明

用来分配所需的内存空间,并返回一个指向它的指针。

//参数  :size -- 内存块的大小,以字节为单位
//返回值:指针 -- 指向已分配大小的内存
//       NULL -- 如果请求失败
void *malloc(size_t size)

介绍一下用法

#include 
#include 
#include 
int main()
{
   char *str;
 
   str = (char *) malloc(15);
   strcpy(str, "hello world");
   printf("String = %s,  Address = %u\n", str, str);

   free(str);
   return(0);
}

编译结果如下,大家自行体会malloc的用法。

malloc的实现机制

当我们了解了malloc的作用应用范围以及用法之后,我们先看看它是怎么实现内存分配的,在此我们需要先了解几个概念。

虚拟内存地址与物理内存地址

为了简单便捷,现代操作系统在汇编程序(或机器语言)层面,处理内存地址时,都是使用虚拟内存地址。这样每个进程可以自己独享一片2N字节的内存,其中N是机器位数。例如在64位CPU和64位OS下,每个进程的虚拟地址空间为2*64 Byte

虚拟地址的作用主要是简化程序的编写及方便操作系统对进程间内存的隔离管理,由于在机器语言层面都是采用虚拟地址,操作系统会将虚拟内存和实际的物理内存进行映射,CPU芯片上叫做存储器管理单元(Memory Management Unit,MMU)的专用硬件,利用存放在主存中的查询表来动态翻译虚拟地址,才能实现对真实内存数据的操作。

页与地址构成

在现代操作系统是以页(Page)为单位。一个内存页是一段固定大小的连续内存地址的总称。

内存地址可以分为页号和页内偏移量。下面以64位机器,4G物理内存,4K页大小为例,虚拟内存地址和物理内存地址的组成如下:


内存地址构成

上面是虚拟内存地址,下面是物理内存地址。由于页大小都是4K,所以页内都是用低12位表示,而剩下的高地址表示页号。

MMU映射单位并不是字节,而是页,这个映射通过查一个常驻内存的数据结构页表来实现。现在计算机具体的内存地址映射比较复杂,为了加快速度会引入一系列缓存和优化。下面给出一个经过简化的内存地址翻译示意图。


运行时堆

在已经映射的内存空间结尾有一个break指针,这个指针下面是映射好的内存,可以访问,上面则是未映射的访问,不能访问。可以通过系统调用sbrk(位移量)确定brk指针的位置,同时返回brk指针的位置,达到申请内存的目。brk(void *addr)系统调用可以直接将brk设置为某个地址,成功返回0,不成功返回-1。而rlimit则是限制进程堆内存容量的指针。

malloc内存分配原理

malloc采用推进brk指针来增加堆的有效区域来申请内存空间分配内存,维护一个内存空闲链表,当申请内存空间时,搜索内存空闲链表,找到适配的空闲内存空间,然后将空间分割成两个内存块,一个变成分配块,一个变成新的空闲块。如果没有搜索到,那么就会用sbrk()才推进brk指针来申请内存空间。

为什么避免使用

这其实要从malloc和free的设计上考虑,通常,它们是基于列表分配器算法将内存池组织到单个链表中的连续位置,使用分配器来管理该链表,实际上就是寻找空闲位置。

但是在极端的safety-critical系统中,malloc常常极其不可预测,在多核系统上进行多线程开发时是个难题,具体有以下几点。

内存有限,多次申请不易管理

嵌入式的内存就只有几十K到几百K,程序在运行时向系统申请内存使用,在使用完毕后,需要显式的释放,不然后果很严重,在多次申请复杂的逻辑开发时,这就要求程序员对动态分配的内存很了解

碎片

在c语言中的malloc进行的动态内存分配和嵌入式系统中使用到堆区的内存分配会产生内存碎片,例如

char  *p;
if(p=char* malloc(0)==NULL){
  printf("NULL\n");
}
else{
  printf("NOT  NULL");
}

实际上最终出现的并不是NULL,而是NOT NULL 这就说明了进行动态内存分配的时候产生了内存碎片

  • 内部碎片的产生

所有的内存分配必须起始于可被 4、或8 或16 整除的地址,或者因为MMU的分页机制的限制,决定内存分配算法仅能把预定大小的内存块分配给客户。

假设在请求一个17字节的内存块时,它可能会获得20字节、24字节等稍大一点的字节,因此由所需的大小需要四舍五入,而产生的多余空间就叫内部碎片。

  • 外部碎片的产生

频繁的分配与回收物理页面会导致大量的、连续且小的页面块夹杂在已分配的页面中间,就会产生外部碎片。

内存泄漏

分配出去的内存在使用之后没有释放掉,没有回收,长此以往,会造成没有足够的内存可以分配。一般表现为运行时间越长,占用的内存越多,最终导致系统奔溃。

所以在进行硬件内存比较小的外围开发的时候,一定要避免内存泄漏,合理的使用内存空间,才能更好的发挥硬件的作用。

怎么解决

在继续使用malloc和free的情况下

正确使用malloc函数分配内存

在实际应用中,我们可以试着把连续的大块内存按分区来管理。每个分区中包含整数个大小相同的内存块。如图所示:

利用这种机制,就可以得到和释放固定大小的内存块。这样内存的申请和释放函数的执行时间就是确定的了,但是特定的内存块在释放时,必须重新回到它原本属于的内存分区。

正确使用free函数释放内存

free函数其实就做了一件事:斩断指针变量和这块内存的对应关系,在使用free(p) 函数内存释放后,指针变量p本身保存的地址并没有改变,那我们必须需重新把p的值变为NULL:即p = NULL

自定义一套内存分配器

尽量避免使用malloc时,我们可以自定义一套本地线程内存分配器,基于栈的分配器,以及基于本地线程的分配器,通过为每个线程分配特定的内存池来避免冲突

最后

在嵌入式系统中,并不是说不使用malloc()和free()管理内存,而是说在使用时需要让我们的代码更具预测性,避免不必要的未知bug产生。


往期推荐




     

免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

2024年4月11日,中国——意法半导体的ST25R100近距离通信(NFC)读取器芯片独步业界,集先进的技术功能、稳定可靠的通信连接和低廉的成本价格于一身,在大规模制造的消费电子和工控设备内,可以提高非接触式互动功能的...

关键字: 嵌入式 数据读取器 芯片

单片机是一种嵌入式系统,它是一块集成电路芯片,内部包含了处理器、存储器和输入输出接口等功能。

关键字: 单片机 编写程序 嵌入式

深圳2024年4月23日 /美通社/ -- 全球AI解决方案与工业级存储领导品牌宜鼎国际 (Innodisk)持续深化边缘AI布局,今(23)日发表全球首创"MIPI over Type-C"独家技术,让旗下嵌入式相机模...

关键字: AI 嵌入式 相机

为增进大家对嵌入式主板的认识,本文将对嵌入式主板以及嵌入式主板常见问题及其解决方法予以介绍。

关键字: 嵌入式 指数 主板

为增进大家对嵌入式系统的认识,本文将对嵌入式系统、嵌入式系统的特点予以介绍。

关键字: 嵌入式 指数 嵌入式系统

为增进大家对嵌入式的认识,本文将对嵌入式、嵌入式工作相关的内容予以介绍。

关键字: 嵌入式 指数 嵌入式技术

机器人操作系统(ROS)驱动程序基于ADI产品而开发,因此可直接在ROS生态系统中使用这些产品。本文将概述如何在应用、产品和系统(例如,自主导航、安全气泡地图和数据收集机器人)中使用和集成这些驱动程序;以及这样将如何有助...

关键字: 电机控制器 机器人 嵌入式

支持高达48V@5A的PD受电模式,达到目前USB PD最高标准。

关键字: 嵌入式 开发板

【2024年4月8日,德国慕尼黑讯】低碳化和数字化是当今时代人们面临的两大核心挑战,人类社会需要依靠创新和先进的技术,才能破除挑战、推动转型进程。在德国纽伦堡举办的2024国际嵌入式展(Embedded World 20...

关键字: 半导体 微控制器 嵌入式

TDK 株式会社(TSE:6762)进一步扩充 Micronas 嵌入式电机控制器系列 HVC 5x,完全集成电机控制器与 HVC-5222D 和 HVC-5422D,以驱动小型有刷(BDC)、无刷(BLDC)或步进电机...

关键字: 嵌入式 电机控制器 内存
关闭
关闭