内存管理
一、什么是内存管理?为什么要进行内存管理?
标准 C 库中的 malloc()和 free()也可以实现动态内存管理,但是如下原因限制了其使用:
● 这些函数在小型嵌入式系统中并不总是可用的,小型嵌入式设备中的 RAM 不足。
● 它们的实现可能非常的大,占据了相当大的一块代码空间。
● 他们几乎都不是安全的。
● 它们并不是确定的,每次调用这些函数执行的时间可能都不一样。
● 它们有可能产生碎片。
● 这两个函数会使得链接器配置得复杂。
pvPortMalloc()来替代 malloc(),vPortFree()函数来替代 free()。
FreeRTOS 的 V9.0.0 版本为我们提供了 5 种内存管理算法,分别是 heap_1.c、heap_2.c、heap_3.c、heap_4.c、heap_5.c,
在FreeRTOS\Source\portable\MemMang这个位置可以找到
二、什么是内存碎片?内存碎片的弊端?
由图可以看到,但内存分配一些空间后,然后再被释放掉,但是释放的这些内存的大小可能不满足新申请的内存的大小,就会变成一个碎片一个碎片的。就会导致仍然有内存,但是无法被使用的后果,导致系统无法运行。
内存管理算法主要就是解决内存碎片的合并的问题。
三、内存分配方法
FreeRTOS 中的内存堆为ucHeap[],大小为configTOTAL_HEAP_SIZE。
由图可以看到,在freeRTOS.config文件中如果配置了宏为1,则需要用户自定义内存堆,如果配置为0,则使用ucHeap[]编译器定义
一般主要使用三个函数 *pvPortMalloc()内存申请
vPortFree()内存释放
xPortGetFreeHeapSize()获取当前未分配内存的大小
第一种内存分配方法Heap1.c
(这个没有内存释放函数)
这种分配方法就是给定一个大数组,然后从这个大的数组中去进行内存的分配。
使用这两个变量对系统管理的内存进行跟踪。
第一个变量的含义是:定位下一个空闲内存堆的位置,实际上也就是记录已经被分配内存的大小,在每次申请内存成功后,都会增加内存的字节数目。
第二个变量的含义:这是一个指针,用来指向对齐后的内存的起始地址。
使用一个数组作为堆内存,但这个数组的起始地址并不一定是内存的起始地址,可能会进行内存对齐管理
什么是内存对齐?
对于Cortx-M内核系列的芯片,一般使用8字节的对齐方式
通过图片可以看到,数组分配的起始地址并不一定能被8整除,所以需要舍去一些字节,结尾的地方也相同。这样就会有一个对齐后的内存地址,将这个地址存放到上边所述的指针变量中。
内存分配
当申请的字节不是8字节的整数时,仍然会按照8字节的空间进行分配。例如申请30个字节,通过8字节对齐,就会分配32个字节的空间,然后指针变量就会指向32个字节的位置,然后第一个变量就会存放已经被申请的字节的大小(32个字节)
第二种内存分配方法Heap2.c
(这个有内存释放函数,但是不会将内存碎片进行合并)
内存块的概念
这个图很好的解释了下边的这段话
定义了一个内存块,用来指向链表中的下一个空闲内存块,这个需要占8个字节的大小
例如申请了12个字节大小的内存,它会分配24个字节的大小,其中8个字节用来存储指向链表的下一个字节和当前内存块的大。
加入了链表的思想,相当于将内存分成多个块,然后制作成链表,可以用来指向下一个成员
第三种内存分配方法Heap3.c
这个与第一种和第二种的不同在于(不是使用数组进行内存分配的,而是对c中的malloc()和Free()进行封装)
heap_3.c 方法只是简单的封装了标准 C 库中的 malloc()和 free()函数,并且能满足常用的编译器。重新封装后的 malloc()和 free()函数具有保护功能,采用的封装方式是操作内存前挂起调度器、完成后再恢复调度器。
第四种内存分配方法Heap4.c(常用)
这个与第一种和第二种的不同在于(多了内存块合并的算法,可以很好的解决内存碎片的问题)
第五种内存分配方法Heap5.c
保留由heap4.c的内存碎片合并短发,添加了可以将两个RAM一起作为内存堆进行使用的算法。
允许内存堆跨越多个不连续的内存段
例如;有的芯片的PAM小,此时会外接一个RAM,这时stm32就会选择一个RAM进行使用,但是如果使用这个内存分配方法,就可以可以将两个RAM一起作为内存堆进行使用。
如果使用 heap_5 的话,在调用 API 函数之前需要先调用函数 vPortDefineHeapRegions ()来对内存堆做初始化处理,在 vPortDefineHeapRegions()未执行完之前禁止调用任何可能会调用pvPortMalloc()的 API 函数!
可以看到将内部RAM和外部RAM放到一个数组中,将两个RAM合并为一个数组(内存堆)
总结:第一种是只能进行内存分配,第二种是可以进行内存释放,但是释放后可能会有内存碎片的产生,第三种是对C中的内存分配和释放进行了封装,第四种是在第二种的基础上添加了内存碎片合并的算法,第五种是在第四种的基础上进行了不连续的内存段的合并的算法。
第一第二第四第五都是使用数组来做内存分配管理的方法。
在内存分配管理的时候需要理解什么是内存对齐和实际所分配的内存空间