天津高端网站设计公司,美食网页设计图,微营销平台系统,wordpress静态内存管理
在计算系统中#xff0c;通常存储空间分为两种#xff1a;内部存储空间和外部存储空间。
内部存储空间通常访问速度比较快#xff0c;能够按照变量地址随机访问#xff0c;也就是我们通常所说的RAM#xff08;随机存储器#xff09;#xff0c;可以把它理解为…内存管理
在计算系统中通常存储空间分为两种内部存储空间和外部存储空间。
内部存储空间通常访问速度比较快能够按照变量地址随机访问也就是我们通常所说的RAM随机存储器可以把它理解为电脑的内存。
外部存储空间内所保存的内容相对来说比较固定即使掉电后数据也不会丢失这就是通常所讲的ROM只读存储器可以把它理解为电脑的硬盘。
计算机系统中变量、中间数据一般存放在RAM中只有在实际使用时才将它们从RAM调入到CPU中进行运算。
一些数据需要的内存大小需要在程序运行过程中根据实际情况确定这就要求系统具有对内存空间进行动态管理的能力在用户需要一段内存空间时向系统申请系统选择一段合适的内存空间分配给用户用户使用完毕后再释放回系统以便系统将该段内存空间回收再利用。
内存管理的功能特点
由于实时系统中对时间的要求非常严格内存管理往往要比通用操作系统要求苛刻得多
分配内存的时间必须是确定的。一般内存管理算法是根据需要存储的数据的长度在内存中去寻找一个与这段数据相适应的空闲内存块然后将数据存储在里面。而寻找这样一个空闲内存块所耗费的时间是不确定的因此对于实时系统来说这就是不可接受的实时系统必须要保证内存块的分配过程在可预测的确定时间内完成否则实时任务对外部事件的响应也将变得不可确定。随着内存不断被分配和释放整个内存区域会产生越来越多的碎片因为在使用过程中申请了一些内存其中一些释放了导致内存空间中存在一些小的内存块它们地址不连续不能够作为一整块的大内存分配出去。系统中还有足够的空闲内存但因为它们地址并非连续不能组成一块连续的完整内存块会使得程序不能申请到大的内存。对于通用系统而言这种不恰当的内存分配算法可以通过重新启动系统来解决每个月或者数个月进行一次但是对于那些需要常年不间断地工作于野外的嵌入式系统来说就变得让人无法接受了。嵌入式系统的资源环境也是不尽相同有些系统的资源比较紧张只有数十KB的内存可供分配而有些系统则存在数MB的内存如何为这些不同的系统选择适合它们的高效率的内存分配算法就将变得复杂化。
RT-Thread操作系统在内存管理上根据上层应用及系统资源的不同有针对性地提供了不同的内存分配管理算法。 总体上可分为两类内存堆管理与内存池管理而内存堆管理又根据具体内存设备划分为三种情况
针对小内存的分配管理小内存管理算法针对大内存的分配管理slab管理算法针对多内存堆的分配情况memheap管理算法
BIN文件与HEX文件
HEX文件是包括地址信息的而BIN文件格式只包括了数据本身。 在烧写或下载HEX文件的时候一般不需要用户指定地址因为HEX文件内部的信息已经包括了地址。 而烧写BIN文件的时候用户是一定需要指定地址信息的。
BIN文件格式 对二进制文件而言其实没有“格式”。文件只是包括了纯粹的二进制数据。
程序内存分布
一般MCU包含的存储空间有片内Flash与片内RAMRAM相当于内存Flash相当于硬盘。 编译器会将一个程序分类分为好几个部分分别存储在MCU不同的存储区。
Keil工程在编译完之后会有相应的程序所占用的空间提示信息如下所示
linking...
Program Size: Code48008 RO-data5660 RW-data604 ZI-data2124
After Build - User command \#1: fromelf --bin.\\build\\rtthread-stm32.axf--output rtthread.bin
.\\build\\rtthread-stm32.axf - 0 Error(s), 0 Warning(s).
Build Time Elapsed: 00:00:07上面提到的Program Size包含以下几个部分
Code代码段存放程序的代码部分。RO-data只读数据段存放程序中定义的常量。RW-data读写数据段存放初始化为非零值的全局变量。ZI-data0数据段存放未初始化的全局变量以及初始化为0的变量。
编译完工程会生成一个.map的文件该文件说明了各个函数占用的尺寸和地址在文件的最后几行也说明了上面几个字段的关系。
Total RO Size (Code RO Data) 53668 ( 52.41kB)
Total RW Size (RW Data ZI Data) 2728 ( 2.66kB)
Total ROM Size (Code RO Data RW Data) 53780 ( 52.52kB)RO Size包含了Code及RO-data表示程序占用Flash空间的大小。RW Size包含了RW-data及ZI-data表示运行时占用的RAM的大小。ROM Size包含了Code、RO-data以及RW-data表示烧写程序时占用的Flash空间的大小。
程序运行之前需要有文件实体被烧录到STM32的Flash中一般是bin或者hex文件该被烧录文件称为可执行映像文件。 左边部分是可执行映像文件烧录到STM32后的内存分布它包含RO段和RW段两个部分
其中RO段中保存了Code、RO-data的数据RW段保存了RW-data的数据由于ZI-data都是0所以未包含在映像文件中。
STM32在上电启动之后默认从Flash启动启动之后会将RW段中的RW-data初始化的全局变量搬运到RAM中但不会搬运RO段即CPU的执行代码即CPU的执行代码从Flash中读取另外根据编译器给出的ZI地址和大小分配出ZI段并将这块RAM区域清零。
其中动态内存堆为未使用的RAM空间应用程序申请和释放的内存块都来自该空间。
rt_uint8_t* msg_ptr;
msg_ptr (rt_uint8_t *)rt_malloc(128);
rt_memset(msg_ptr, 0, 128);代码中的msg_ptr指针指向的128字节内存空间位于动态内存堆空间中。
内存堆管理
内存堆管理用于管理一段连续的内存空间。 RT-Thread将ZI段结尾处到内存尾部的空间用作内存堆。
内存堆可以在当前资源满足的情况下根据用户的需求分配任意大小的内存块。 而当用户不需要再使用这些内存块时又可以释放回堆中供其它应用分配使用。
为了满足不同的需求提供了不同的内存管理算法分别是小内存管理算法slab管理算法和memheap管理算法。
小内存管理算法主要针对系统资源比较少一般用于小于2MB内存空间的系统而slab内存管理算法则主要是在系统资源比较丰富时提供了一种近似多内存池管理算法的快速算法。 除上述之外RT-Thread还有一种针对多内存堆的管理算法即memheap管理算法。memheap方法适用于系统存在多个内存堆的情况它可以将多个内存“粘贴”在一起形成一个大的内存堆用户使用起来会非常方便。
这几类内存堆管理算法在系统运行时只能选择其中之一或者完全不使用内存堆管理器他们提供给应用程序的API接口完全相同。
小内存管理算法
小内存管理算法是一个简单的内存分配算法。 初始时它是一块大的内存。当需要分配内存块时将从这个大的内存块上分割出相匹配的内存块然后把分割出来的空闲内存块还回给堆管理系统中。 每个内存块都包含一个管理用的数据头通过这个头把使用块与空闲块用双向链表的方式链接起来如图所示 每个内存块不管是已分配的内存块还是空闲的内存块都包含一个数据头其中包括
magic变数或称为幻数它会被初始化成0x1ea0即英文单词heap用于标记这个内存块是一个内存管理用的内存数据块实质也是一个内存保护字如果这个区域被改写那么就意味着这块内存块被非法改写正常情况下只有内存管理器才会去碰这块内存。used指示出当前内存块是否已经分配。
内存管理的表现主要体现在内存的分配与释放上小型内存管理算法可以用以下例子体现出来。 空闲链表指针lfree初始指向32字节的内存块。 当用户线程要再分配一个64字节的内存块时但此lfree指针指向的内存块只有32字节并不能满足要求内存管理器会继续寻找下一内存块当找到再下一块内存块128字节时它满足分配的要求。
因为这个内存块比较大分配器将把此内存块进行拆分余下的内存块52字节继续留在lfree链表中。
在每次分配内存块前都会留出12字节数据头用于magic、used信息及链表节点使用。 返回给应用的地址实际上是这块内存块12字节以后的地址前面的12字节数据头是用户永远不应该碰的部分。
释放时则是相反的过程但分配器会查看前后相邻的内存块是否空闲如果空闲则合并成一个大的空闲内存块。
slab管理算法
RT-Thread 的 slab 分配器是在 DragonFly BSD 创始人 Matthew Dillon 实现的slab分配器基础上针对嵌入式系统优化的内存分配算法。
RT-Thread的slab分配器实现主要是去掉了其中的对象构造及析构过程只保留了纯粹的缓冲型的内存池算法。
slab分配器会根据对象的大小分成多个区zone也可以看成每类对象有一个内存池如下图所示 一个zone的大小在32K到128K字节之间分配器会在堆初始化时根据堆的大小自动调整。 系统中的zone最多包括72种对象一次最大能够分配16K的内存空间如果超出了16K那么直接从页分配器中分配。
每个zone上分配的内存块大小是固定的能够分配相同大小内存块的zone会链接在一个链表中而72种对象的zone链表则放在一个数组zone_array[]中统一管理。
下面是内存分配器主要的两种操作 内存分配 假设分配一个32字节的内存slab内存分配器会先按照32字节的值从zone array链表头数组中找到相应的zone链表。
如果这个链表是空的则向页分配器分配一个新的zone然后从zone中返回第一个空闲内存块。
如果链表非空则这个zone链表中的第一个zone节点必然有空闲块存在否则它就不应该放在这个链表中那么就取相应的空闲块。
如果分配完成后zone中所有空闲内存块都使用完毕那么分配器需要把这个zone节点从链表中删除。
内存释放 分配器需要找到内存块所在的zone节点然后把内存块链接到zone的空闲内存块链表中。 如果此时zone的空闲链表指示出zone的所有内存块都已经释放则zone是完全空闲的那么当zone链表中全空闲zone达到一定数目后系统就会把这个全空闲的zone释放到页面分配其中去。
memheap管理算法
memheap管理算法适用于系统含有多个地址可不连续的内存堆。使用memheap内存管理可以简化系统存在多个内存堆时的使用当系统中存在多个内存堆的时候用户只需要在系统初始化时将多个所需的 memheap 初始化并开启 memheap 功能就可以很方便地把多个 memheap地址可不连续粘合起来用于系统的 heap 分配。
memheap工作机制如图所示首先将多块内存加入memheap_item链表进行粘合。
当分配内存块时会先从默认内存堆去分配内存当分配不到时会查找memheap_item链表尝试从其它的内存堆上分配内存块。应用程序不用关心当前分配的内存块位于哪个内存堆上就像是在操作一个内存堆。
内存堆配置和初始化
在使用内存堆时必须要在系统初始化的时候进行堆的初始化可以通过下面的函数接口
void rt_system_heap_init(void *begin_addr, void *end_addr);这个函数会把参数begin_addrend_addr区域的内存空间作为内存堆来使用。
begin_addr堆内存区域起始地址end_addr堆内存区域结束地址
在使用memheap堆内存时必须要在系统初始化的时候进行堆内存的初始化可以通过下面的函数接口完成
rt_err_t rt_memheap_init(struct rt_memheap *memheap, const char *name, void *start_addr, rt_uint32_t size)如果有多个不连续的memheap可以多次调用该函数将其初始化加入memheap_item链表。
内存堆的管理方式
对内存堆的操作如下图所示包含初始化、申请内存块、释放内存所有使用完成后的动态内存都应该被释放以供其它程序的申请使用。 分配和释放内存块 从内存堆上分配用户指定大小的内存块函数接口如下
void *rt_malloc(rt_size_t nbytes);rt_malloc函数会从系统堆空间中找到合适大小的内存块然后把内存块可用地址返回给用户。
对rt_malloc的返回值进行判空是非常有必要的。应用程序使用完从内存分配器中申请的内存后必须及时释放否则会造成内存泄漏。
内存池
内存堆管理器可以分配任意大小的内存块非常灵活和方便。 但其也存在明显的缺点
分配效率不高每次分配时都要空闲内存块查找。容易产生内存碎片为了提高内存分配的效率并且避免内存碎片。
内存池Memory Pool 内存池是一种内存分配方式用于分配大量大小相同的小内存块它可以极大地加快内存分配与释放的速度且能尽量避免内存碎片化。
内存池控制块
内存池控制块是操作系统用于管理内存池的一个数据结构它会存放内存池的一些信息例如内存池数据区域开始地址内存块大小和内存块列表等也包含内存块与内存块之间连接用的链表结构因内存块不可用而挂起的线程等待事件集合等。
在RT-Thread实时操作系统中内存池控制块由结构体struct rt_mempool表示。另外一种C表达方式rt_mp_t表示的是内存块句柄在C语言中的实现是指向内存池控制块的指针详细定义情况以下代码
struct rt_mempool
{struct rt_object parent;void *start_address; //内存池数据区域开始地址rt_size_t size; //内存池数据区域大小rt_size_t block_size; //内存块大小rt_uint8_t *block_list; //内存块链表/* 内存池数据区域中能够容纳的最大内存块数 */rt_size_t block_total_count;/* 内存池空闲的内存块数 */rt_size_t block_free_count;/* 因为内存块不可用而挂起的线程列表 */rt_list_t suspend_thread;/* 因为内存块不可用而挂起的线程数 */rt_size_t suspend_thread_count;
}
typedef struct rt_mempool* rt_mp_t;内存块分配机制
内存池在创建时先向系统申请一大块内存然后分成同样大小的多个小内存块小内存块直接通过链表连接起来此链表也称为空闲链表。
每次分配的时候