一个zone的大小在32k ~ 128k字节之间,分配器会在堆初始化时根据堆的大小自动调整。系统中最多包括72种对象的zone,最大能够分配16k的内存空间,如果超出了16k那么直接从页分配器中分配。每个zone上分配的内存块大小是固定的,能够分配相同大小内存块的zone会链接在一个链表中,而72种对象的zone链表则放在一个数组(zone arry)中统一管理。
下面是分配器主要的两种操作:
内存分配: 假设分配一个32字节的内存,SLAB内存分配器会先按照32字节的值,从zone array链表表头数组中找到相应的zone链表。如果这个链表是空的,则向页分配器分配一个新的zone,然后从zone中返回第一个空闲内存块。如果链表非空,则这个zone链表中的第一个zone节点必然有空闲块存在(否则它就不应该放在这个链表中),那么就取相应的空闲块。如果分配完成后,zone中所有空闲内存块都使用完毕,那么分配器需要把这个zone节点从链表中删除。
内存释放:分配器需要找到内存块所在的zone节点,然后把内存块链接到zone的空闲内存块链表中。如果此时zone的空闲链表指示出zone的所有内存块都已经释放,即zone是完全空闲的,那么当zone链表中全空闲zone达到一定数目后,系统就会把这个全空闲的zone释放到页面分配器中去。
这个算法比较复杂,下面就从代码层面来看看它的具体实现方法。
这个算法有如下几个比较重要的函数:
void *rt_page_alloc(rt_size_t npages) void rt_page_free(void *addr, rt_size_t npages) sta
tic void rt_page_init(void *addr, rt_size_t npages) void rt_system_heap_init(void *begin_addr, void *end_addr) void *rt_malloc(rt_size_t size) void rt_free(void *ptr)
这里面有个rt_page,这个函数是用来为zone分配内存而使用的。
```struct rt_page_head
{
struct rt_page_head next; / next valid page /
rt_size_t page; / number of page */
/* dummy */
char dummy[RT_MM_PAGE_SIZE - (sizeof(struct rt_page_head *) + sizeof(rt_size_t))];
};```
因为是针对大内存所使用的算法,所以其把给zone使用的内存分配单位定为了4096(4K),其算法与第二种内存算法基本一致,只有一点不同,就是他的结构中包含了每个内存控制块所包含的page数量(对应于算法2的字节数),所以不再需要把所有控制结构用放入一个链表中,而是直接把空闲的控制块放入链表,一旦控制块被分配,将其移出整个链表就好了。
为了理解代码的实现,这里觉得只要知道了page,zone,chunk的关系就可以了。这3个东西均是内存块,但是却是不同的功能。
page很简单,其实就是给zone分配内存时候的最基本的内存单位,RTT中默认为4K。
zone其实就是一个内存池,它的内部被划分为n个相同大小的chunk,这个结构非常类似于第一个内存分配算法mempool。这点从它所包含的成员也可以看出。
```typedef struct slab_zone
chunk就是zone中分配内存的最小单位,类比于mempool中的block,每个zone中chunk的大小都相同。有一个链表将所有未分配的chunk链接起来,当chunk被分配之后就会被移出这个链表。
```typedef struct slab_chunk
RTT内存管理相关的内容都大概分析完了。之后会抽时间继续分析RTT其他部分的代码。
原作者:yushigengyu