为什么我们需要内存管理——RT-Thread 学习笔记

 首先这个问题是我在学习RT-Thread内存管理的时候也在问自己的,因为自己在学习单片机的时候堆内存这个概念学习也是云里雾里,感觉只知道叫内存,已经有 RAM 、ROM 、Flash……这几个东西,具体自己也就混过去了。想到很多考试都考完了,想着用一天来把他好好理解一下。所以就有了这篇文章,具体我有参考几篇文章,文章底部我会放出大佬们的文章链接。一下也是我学习的思路,如果大家有想看的,也可以选择跳着看。

FLASH 和 RAM基本概念与联系

在为什么想清内存管理是什么之前,我们先了解我刚刚说到的那几个名词吧 RAM 、ROM 、Flash 。百度百科:存储器是用来存储程序和各种数据信息的记忆部件。总结关键词:程序、数据信息、记忆。当然存储器的分类方式有很多大家可以自行上百度啦,我这里用的是图中两个分类:

从图上可以看出存储器有分易失存储器与非易存储器两大类。从文本含义可以看出一个容易失去一个不容易失去。(可以方便记忆与理解)并且我个人觉得记住一些英语单词能够给记录模块功能有很大帮助,所以我在下文中会不时给出英文注释。

RAM

SRAM(Static random-access memory

静态随机存取存储器(英语:Static random-access memory,缩写SRAM)是随机存取存储器的一种。所谓的“静态”,是指这种存储器只要保持通电,里面存储的数据就可以恒常保持[1]。相对之下,动态随机存取存储器(DRAM)里面所存储的数据就需要周期性地更新。然而,当电力供应停止时,SRAM存储的数据还是会消失(被称为易失性存储器),这与在断电后还能存储资料的ROM闪存是不同的。 

DRAM (Dynamic random-access memor)

RAM 总结

1. 随机存储,掉电的话数据都会失去。

2.  SRAM 速度非常快,是目前读写最快的存储设备,但是它也非常昂贵,所以只在要求很苛刻的地方使用,譬如CPU的一级缓冲。 DRAM 保留数据的时间很短,速度也比 SRAM 慢,不过它还是比任何的 ROM 都要快,但从价格上来说 DRAM 相比 SRAM 要便宜很多,计算机内存就是 DRAM的。

FLASH

早期的闪存只要进行一次删除就会连带清除掉所有的资料,但目前已可以精确到对指定的资料进行单个删除。与传统的硬盘相比,闪存有更佳的动态抗震性,不会因为剧烈晃动而造成资料丢失;闪存在被做成储存卡时非常坚固牢靠,可以浸在水中,也可抵抗高压力和极端温度;并且闪存属于“非易失性固态存储”,非易失性指的是在保存文件时不需要消耗电力。基于以上这些优点,使得闪存非常适用于需要游历各种场所并需要随时存档的电子设备,因此在小型的可移动电子设备中大放异彩。闪存的出现迅速取代了造价高昂的普通EEPROM或需要保持供电才能保存数据的SRAM

STM32单片机内部的FLASH为 NOR FLASH。 Flash 相对容量大,掉电数据不丢失,主要用来存储 代码,以及一些掉电不丢失的用户数据。

STM32的内存架构

了解完上面的存储器结构,我们先了解内存架构吧,因为我课程用到的代码是基于 STM32 ,以及我看的文章也是 STM32 ,所以开始了解STM32的内存架构吧。(感觉有点啰嗦但是为了说清楚还是说了)。现在我们就开始看看 Cortex_M3 的内存架构叭,你可能在想为什么要分析 Cortex_M3 而不是 STM32 ?

因为 STM32 是单片机!!!Cortex 才是处理器!!!STM32是基于Cortex处理器的单片机!所以我们分析他。这个问题我也有过,这里也顺便说一下。可以自己也去看一下简介当作课外阅读👇

STM官网

因为我的课程单片机是基于 Cortex_M3 处理器所以我以此来学习。

Cortex-M3的存储器存储器映射

可以看到其实存储器里不同的地址对应不同的存储空间。这个是Cortex-M3中文手册里的,我其实觉得不太好理解对于我而言,所以我网上找了一个图很好理解():

 这张图加上图上的注释可以很清楚的看到不同区的不同地址按顺序展示出来。这里因为stm32是32位的单片机,所以指针可以指向2^32=(2^2)*(2*10)*(2*10)(2*10)=4GBd的地址空间,也就是0x00000000 - 0xFFFFFFFF(可以那程序员计算器)。

STM32F103ZE 的内存映射

 为了方便学习,我们在查看这个映射图的时候只需要注意两个地址,一个是 SRAM ,还有一个是FLASH,因为我们上文说过存储器就包括这两个东西,至于其他的,在映射表内我没看到,应该有,但是在这里我就不详细去找他们了,重点不是其他的。也就是说STM32 的内存管理起始就是对 0X0800 0000 开始的 Flash 部分 和 0x2000 0000 开始的 SRAM 部分使用管理。

FLASH 存储下载的程序,全局变量和静态变量,在运行时,系统会把这些变量搬运到 SRAM 。同时根据代码定义,未初始化的全局变量在开始运行时会在 SRAM 开辟一块空间保证使用。

SRAM 是存储运行程序中的数据,所以,在运行时,我们定义的没有初始化的全局变量和没有初始化的静态变量,使用的局部变量,堆栈都放在 SRAM 中。

 搞清楚了存储区域的地址,那我们就开始了解 STM32 的存储数据段叭。

STM32 的存储数据段

一个程序被加载到内存中,这块内存首先就存在两种属性:静态分配内存和动态分配内存。 
静态分配内存:是在程序编译和链接时就确定好的内存。 
动态分配内存:是在程序加载、调入、执行的时候分配/回收的内存。

任何一个程序本质上都是由 bss 段、data 段、text 段三个组成的。

C语言上分为栈、堆、bss、data、code段

后三段内存就组成了我们编写的程序的本体,但是一个程序运行起来,还需要更多的数据和数据间的交互,否则这个程序就是死的,无用的。所以我们还需要为更多的数据和数据交互提供一块内存——堆栈。

网上区分的讲法有很多,但我找到一个非常方便归类和理解的图:

bss段

Block Started by Symbol

储存未初始化的,或初始化为0的全局变量和静态变量。bss段属于静态内存分配,所以放在RAM里。

data段

数据段,储存已初始化且不为0的全局变量和静态变量(全局静态变量和局部静态变量)。

static声明的变量放在data段。

数据段属于静态内存分配,所以放在RAM里,准确来说,是在程序运行的时候需要在RAM中运行。

text段

代码段,储存程序代码。也就是存放CPU执行的机器指令(machineinstructions)。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读(某些架构也允许代码段为可写,即允许修改程序)。

在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

放在Flash里。

constdata段

储存只读常量。const修饰的常量,不管是在局部还是全局

放在Flash 里。

最重要的两个(学习内存最重要,不代表其他不重要哈):

heap(堆)

堆是用于存放进程运行中被动态分配的内存段。他的大小并不固定,可动态扩张或者缩减,由程序员使用malloc()和free()函数进行分配和释放。当调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)。

stack(栈)

栈又称堆栈,是用户存放程序临时创建的局部变量,由系统自动分配和释放。可存放局部变量、函数的参数和返回值(但不包括static声明的变量,static意味着 放在 data 数据段中)。 除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。

大家可以很清楚得看到我们动态申请内存的函数就在堆里,但是我们 RT-Thread 用的是 rt_malloc 是经过内存管理后的 API 函数。这篇文章我主要想说的是为什么要内存管理所以我就不详细说明函数的用法。

那我就不仔细说了,我直接运行个程序看看其内存分布:

 从上图可以看到:

程序占用 Flash = Code + RO-data + RW-data

程序运行时候占用 RAM = RW data + ZI data

heap 和 stack 其实也属于 ZI,只不过他不是程序编译就能确定大小的,必须在运行中才会有大小,而是是变化的。

 有人会好奇我上张的文件如何调出来的,其实是程序生成的.map文件,打开步骤如下:

 有人说这里面没看到堆、栈的占用情况,其实在stm32的启动文件.s里面是有栈堆的设置,只有在程序运行的时候才能看到,但系统有开辟空间给他们。具体可以了解这个文章:

STM32的启动过程 — startup_xxxx.s文件解析

 简单了解完,我就要回到最初的问题:为什么要做内存管理?

内存碎片

内存分配有静态分配和动态分配两种。

静态分配在程序编译链接时分配的大小和使用寿命就已经确定,而应用上要求操作系统可以提供给进程运行时申请和释放任意大小内存的功能,这就是内存的动态分配。

因此动态分配将不可避免会产生内存碎片的问题,那么什么是内存碎片?内存碎片即“碎片的内存”描述一个系统中所有不可用的空闲内存,这些碎片之所以不能被使用,是因为负责动态分配内存的分配算法使得这些空闲的内存无法使用,这一问题的发生,原因在于这些空闲内存以小且不连续方式出现在不同的位置。因此这个问题的或大或小取决于内存管理算法的实现上。

 简单总结归纳一下,就是因为 malloc 在堆上分配内存的时候因为字节对齐的缘故,导致内存碎片的产生。因为在 STM32 上使用 malloc 分配的内存空间是8字节对齐的,即便你用不上8个字节,系统也会给你对齐补上。有一个很形象的图源自一个博主,我这里给出:

内部碎片的产生:

 外部碎片的产生:

 所以我们不能简单的直接使用 malloc 函数在一些大个工程内,需要使用一定的内存管理算法来使内存创建分配合理,利用率高,这也就是为什么 RT-Thread 用到的是 rt_malloc 不直接是 malloc ,其就用到了一些内存管理算法:内存池(memeory pool)(静态内存管理)、小内存管理(动态内存管理)、 SLAB 内存管理(动态内存管理)。

关于 API 函数的具体使用,我在本篇就不说多啦,如果以后有时间的话我会写的。在这里我也给出给我学习带来很多思路引导与参考的文章,同时也包括详细介绍 rt-thread 内存管理的文章,很感谢(可能不全,如果有遗漏的可以联系我):

浅谈 malloc 函数在单片机上的应用

STM32的内存管理相关(内存架构,内存管理,map文件分析)

 内存管理「野火」

stm32堆栈,rom,flash详细理解

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值