单片机内存——堆、栈、变量区以及内存管理的理解

时间:2024-03-21 12:50:10

   1.查看单片机的数据手册的时候我们总是可以看到有一个ram和flash的大小。flash是用于存储程序的,ram用于存储程序运行中的一些变量和长量。以nordic52832为例:

 

单片机内存——堆、栈、变量区以及内存管理的理解

这是noridc采用cortex m4架构后芯片的memoney map,实际上简化一下就是:

单片机内存——堆、栈、变量区以及内存管理的理解

这只是简单的内部存储组成示意图,并不是说地址上52832也是这么排的。事实上按照m4的架构有可能用于硬件相关的寄存器地址会嵌入在flash中间。就像52832的map文件显示的那样。

flash用于存储代码,也就是通常所说的ROM区。现在芯片基本很少用到rom,一般都是flash,只是在存储芯片的引导烧录程序那一小存储部分使用到ROM。

我们的重点在于ram区的讨论;

很多前辈写了很多的帖子关于ram区的使用情况:

1.动态变量区(栈区):用于存储局部变量,函数执行完毕即释放;

2.静态变量区:用于存储全局变量;

3.常量区:用于存储常量;

4.堆区:用于使用malloc应用者申请,free释放;

一开始的时候我会想,既然有全局变量区和静态变量区,为什么还要使用堆区呢?直接一个全局变量不就可以了?

实际在使用的时候会发现,堆区其实是很有必要的。比如说,一个串口每隔一定周期会收到一个1k字节的数据,拿到1k的数据后需要通过蓝牙上传给手机,蓝牙一次只传20个,还不能一直使用for不断调用蓝牙发送函数,需要用定时器辅助完成发送。这个时候这个1k的buff就不能是局部,因为不能一直停在uart接受函数这里,需要用到定时器持续蓝牙。如果使用局部的buff,传输uart接受函数执行完毕后这个1k的buff就释放了,定时器来到的时候数据已经没了。如果使用全局,确实不会出现上述的问题,但是你这1k的ram就只能一直做接受uart数据这一件事,那么malloc的神奇就来,我在收uart的时候申请一下1k的buff,蓝牙传输完成以后就可以free掉,这样其他程序可以反复用到这1k的ram空间;

看到这里会觉得malloc确实好用,但是malloc存在一个问题就是内存碎片,所以才有了内存管理一说。关于内存碎片产生的原理,下面的帖子写的十分清楚:

https://blog.csdn.net/chenyefei/article/details/82534058

https://blog.csdn.net/tong5956/article/details/74937178

很多高人写了很多内存管理的程序;但是在翻阅前辈的代码的时候,看到一种很骚的操作,就是不使用系统的malloc,压缩堆区,以换取静态变量区的大小;

静态变量区的全局变量由编译器申请,地址连续,一直存在。所以不会出现内存碎片的问题。

他是怎么做的呢?

全局定义一个二维数组,以申请一块内存块。然后将这一个内存块分成5大块,这每个5大块在继续分为n小块,对于12345不同的大块,内部的小块大小是不一样的,以适用不同的应用需求;比如,1的内部是n个8字节大小的小块,2的内部是n个16字节大小的小块,.................等等

然后定义好控制结构体,应用malloc的时候就通过这些控制结构体来寻找最合适的小块,然后存进去,往控制结构体里写当前块的信息,以示当前块已经有主,有malloc的时候本区块拒绝服务。free以后也是往这个控制结构体写当前块信息,有malloc的时候同意服务;

一般来说,修改堆栈区的大小,修改启动文件里的Stack_Size      EQU     和  Heap_Size       EQU