使用此管理方式:
#defined RT_USING_HEAP && #defined RT_USING_SLAB
SLAB 將記憶體根據不同的對象切成不同的區 (zone),對象通常是大小,也可看成是一個 zone 代表一個 pool,不同的 zone 放在一個 array 管理。
一個 zone 大小介於 32kB~128kB 之間,最多可以有 72 種 zone;zone 對象大小上上限 16kB,超過由頁分配器分配
- alloc:根據需要的大小,找到對應的 zone 取得記憶體;如假設需要 32kB,我們去尋找對象為 32kB 的 zone。
- 若是該 zone 為空(找不到),直接向頁分配器分配一個新的 zone,取得第一塊 free chunk
- 若非空,直接取得第一塊,如果拿完該 zone 已經沒有 free chunk 頁分配器須將此 zone 刪除
- free:找到對應的 zone 插入至 free list,如果該 zone 的所有 free chunk 都已經釋放完畢,則須將此 zone 整個釋放到分配器裡
結構#
File: slab.c
Zone#
166 | /* |
158 | /* |
Page Allocator#
224 | /* page allocator */ |
Descriptor#
207 | /* |
初始化 heap#
功能 | 回傳值 |
---|---|
初始化 heap | void |
*begin_addr |
*end_addr |
---|---|
記憶體起始位址 | 結束位址 |
337 | /** |
- 向上對齊起始位址,向下對其結束位址
- 檢查是否合法
362 | limsize = heap_end - heap_start; |
- 計算最大的 size,設定頁數量
364 | /* initialize heap semaphore */ |
- 初始化 semaphore,值為 1
369 | /* init pages */ |
- 初始化 page
371 | /* calculate zone size */ |
- 計算 zone 的大小、對象大小的上限及總頁數
381 | RT_DEBUG_LOG(RT_DEBUG_SLAB, ("zone size 0x%x, zone page count 0x%x\n", |
- 最後建立一個陣列紀錄頁的資訊
rt_page_init#
功能 | 回傳值 |
---|---|
初始化頁分配器 | void |
*addr |
npages |
---|---|
存放頁的記憶體位址 | 頁的總數 |
324 | /* |
- 將 page list 設為空,釋放所有的 page
分配記憶體#
rt_malloc#
功能 | 回傳值 | size |
---|---|---|
要求記憶體 | 記憶體位址 | 欲要求的大小 |
467 | /** |
- 如果 size = 0,回傳 NULL
488 | /* |
- 如果 size 超過一個 chunk 的上限,則透過頁分配器來分配
- 且如果失敗了,直接回傳
NULL
499 | /* set kup */ |
- 設定頁的資訊:
- type:
PAGE_TYPE_LARGE
- size:用了幾頁
- type:
- btokup:
&memusage[((rt_uint32_t)(addr) - heap_start) >> RT_MM_PAGE_BITS]
- 找到陣列中與起始位置的差值,位移 12-bit,即除一頁的大小
503 | RT_DEBUG_LOG(RT_DEBUG_SLAB, |
- 要鎖,更新使用大小,跳到
__done
519 | /* lock heap */ |
- 如果 size 小於一個 chunk 的上限,尋找此大小對應的 zone
532 | RT_DEBUG_LOG(RT_DEBUG_SLAB, ("try to malloc 0x%x on zone: %d\n", size, zi)); |
- 如果該 zone 不為空,且此 zone 剩最後一顆可用時,將此 zone 刪除
544 | /* |
- 取得一塊,跳至 done
- 從
uindex
找,這種方式取得的屬於此 zone 最初的 chunk - 如果不行,從 free list 中取得,並從 free list 移除此 chunk;這種的 chunk 是已經被要過,又還回來的
- 從
572 | /* |
- 如果找到的 zone 為空,且
zone_free
不為空:代表有可用的空 zone 可以使用
589 | else |
- 否則需要重新與頁分配器要一個 zone
601 | /* lock heap */ |
- 接著設定每一頁的資訊
616 | /* clear to zero */ |
- 清空整個 zone
618 | /* offset of slab zone struct in zone */ |
- 計算我們要用的對齊法:
- 如果 size 是二的次方,將 off (zone 的頭) 與 size 向上對齊
- 否則直接與 8 向上對齊
629 | z->z_magic = ZALLOC_SLAB_MAGIC; |
- 設定 magic、對應
zone_array
的 index- 最大數量為
zone_size
- off 再除以一個 chunk 的大小 - 目前可用的數量則為最大數量減 1,因為待會會拿走一塊
- 基址為起始位址加上
off,uindex
為 0,這是之後 alloc 時可直接使用這兩個來找到 free chunk - 最後設定 chunk size
- 最大數量為
636 | chunk = (slab_chunk *)(z->z_baseptr + z->z_uindex * size); |
- 拿走第一塊,並將這個 zone 插上對應的 zone array entry
648 | done: |
- 最後回傳找到的 chunk
zoneindex#
功能 | 回傳值 | *bytes |
---|---|---|
尋找傳入的 size 對應 zone array 的 index | index | 傳入的大小 |
397 | /* |
根據不同的 range,將傳入的大小對齊,並平均分配每個 range 有 16 個 zone index
rt_page_alloc#
功能 | 回傳值 | npages |
---|---|---|
要求頁記憶體 | 頁 | 欲要求的頁數 |
236 | void *rt_page_alloc(rt_size_t npages) |
- 如果找到一個頁數大於需求的,選擇此頁,並分割
257 | if (b->page == npages) |
- 如果有一個剛剛好,選擇此頁
- 最後回傳選擇的頁
rt_realloc#
功能 | 回傳值 |
---|---|
增長/縮減記憶體 | 記憶體位址 |
*rmem |
newsize |
---|---|
欲增長/縮減的記憶體位址 | 新的大小 |
670 | /** |
- 如果傳入的
ptr
為空,malloc(size)
- 如果傳入的
size
為 0,free(ptr)
692 | /* |
- 接著檢查此
ptr
所在的頁資訊,如果是 LARGE,代表原來的ptr
是由頁分配器所分配的 - 新
malloc(size)
,並還原資料,釋放舊的記憶體
709 | else if (kup->type == PAGE_TYPE_SMALL) |
- 如果是 SMALL,首先找到歸屬得 zone:
- 透過減掉頁資訊上的 size 乘以頁的大小,即可找到zone的初始位址
- 在
malloc
中,建立 zone 時 size 是從 0 開始填,一頁一頁加一 - 如果新的大小與原本的 chunk 相同,不做事
718 | /* |
- 如果不同,
malloc(size)
,並還原資料,釋放舊的記憶體
rt_calloc#
功能 | 回傳值 |
---|---|
要求一段連續的記憶體 | 記憶體位址 |
count |
size |
---|---|
欲要求的數量 | 一塊的大小 |
738 | /** |
- 與 memheap 相同,一次要一塊
count
乘size
的記憶體 - 清 0 並回傳起始位址
釋放記憶體#
功能 | 回傳值 | *ptr |
---|---|---|
釋放記憶體 | void | 欲釋放的記憶體 |
765 | /** |
- 如果要釋放的記憶體是由頁分配器分配的,根據頁資訊中的 size 來釋放,並清 0
- 實際呼叫
rt_page_free(ptr, size)
來完成
821 | /* lock heap */ |
- 如果是由 zone 分配,找到歸屬的 zone,並將需要釋放的 chunk 插到 free list 上
836 | /* |
- 更新
nfree
,如果本來為 0 ,則需要將此 zone 插回 zone array
845 | /* |
- 如果釋放完這個 chunk 後整個 zone 都釋放完了,我們需要釋放整個 zone
- 這裡還同時確保在同一個 zone array entry 中還有其他的 zone 可以分配
- 接著我們把這個 zone 從 zone array 移除
863 | /* reset zone */ |
- 重設 magic,將這個 zone 插上 free zone,free count 加一
871 | /* release zone to page allocator */ |
- 如果已經有
ZONE_RELEASE_THRESH
(2) 個以上的 free zone,完全釋放一個 zone 給頁分配器- 從 free zone 中移除,free count 減一
- 重設頁資訊:type free、size 0
- 透過
rt_page_free
完成
rt_page_free#
功能 | 回傳值 |
---|---|
釋放頁記憶體 | void |
*addr |
pages |
---|---|
欲釋放的頁 | 欲釋放的大小 |
272 | void rt_page_free(void *addr, rt_size_t npages) |