一个zone的大小在32k ~ 128k字节之间,分配器会在堆初始化时根据堆的大小自动调整。系统中最多包括72种对象的zone,最大能够分配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释放到页面分配器中去。
内存管理的应用场景
RT-Threadd操作系统将内核与内存管理分开实现,操作系统内核仅规定了必要的内存管理函数原型,而不关心这些内存管理函数是如何实现的。这样做大有好处,可以增加系统的灵活性:不同的应用场合可以使用不同的内存分配实现,用户也能自己通过API接口进行对内存的管理,选择对自己更有利的内存管理策略。
内存管理的主要工作是动态划分并管理用户分配好的内存区间,主要是在用户需要使用大小不等的内存块的场景中使用,当用户需要分配内存时,可以通过操作系统的动态内存申请函数索取指定大小内存块,一旦使用完毕,通过动态内存释放函数归还所占用内存,使之可以重复使用。
静态内存管理是当用户需要使用固定长度的内存时,可以使用静态内存分配的方式获取内存,一旦使用完毕,通过静态内存释放函数归还所占用内存,使之可以重复使用。
例如我们需要定义一个float型数组:float Arr[];
但是,在使用数组的时候,总有一个问题困扰着我们:数组应该有多大?在很多的情况下,你并不能确定要使用多大的数组,可能为了避免发生错误你就需要把数组定义得足够大。即使你知道想利用的空间大小,但是如果因为某种特殊原因空间利用的大小有增加或者减少,你又必须重新去修改程序,扩大数组的存储范围。这种分配固定大小的内存分配方法称之为静态内存分配。这种内存分配的方法存在比较严重的缺陷,在大多数情况下会浪费大量的内存空间,在少数情况下,当你定义的数组不够大时,可能引起下标越界错误,甚至导致严重后果。
我们用动态内存分配就可以解决上面的问题。所谓动态内存分配就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。
静态内存管理接口
静态内存的典型场景开发流程:
规划一片内存区域作为静态内存池。
调用rt_mp_create()函数。进行静态内存使用前的创建。
调用rt_mp_alloc()函数。系统内部将会从空闲链表中获取第一个空闲块,并返回该块的用户空间地址。
调用rt_mp_free()函数。将该块内存加入空闲块链表,进行内存的释放。
动态内存管理接口
动态内存的典型场景开发流程:
初始化系统堆内存空间:rt_system_heap_init()。
申请任意大小的动态内存:rt_malloc()。
释放动态内存rt_free()。回收系统内存,供下一次使用。
附:memheap 管理算法
memheap 管理算法适用于系统含有多个地址可不连续的内存堆。使用 memheap 内存管理可以简化系统存在多个内存堆时的使用:当系统中存在多个内存堆的时候,用户只需要在系统初始化时将多个所需的 memheap 初始化,并开启 memheap 功能就可以很方便地把多个 memheap(地址可不连续)粘合起来用于系统的 heap 分配。