使用此管理方式:
#defined RT_USING_HEAP && #defined RT_USING_SMALL_MEM
- 與 memory heap 的做法類似,一開始是一塊大的記憶體,包含 header
- 分配記憶體時適當的切割
- 所有的記憶體塊透過 header 串起來,形成一個雙向鏈結
結構#
File: mem.c
96 | struct heap_mem |
- 此結構即為一個記憶體塊的 header
- 包含了
- magic 碼
0x1ea0
- 使用中標記
- 前一顆與下一顆
- 使用此記憶體的 thread 名稱(選)
- magic 碼
初始化 heap#
功能 | 回傳值 |
---|---|
初始化 heap | void |
*begin_addr |
*end_addr |
---|---|
記憶體起始位址 | 結束位址 |
183 | /** |
- 向上對齊起始位址與向下對齊結束位址
198 | /* alignment addr */ |
- 接著檢查起始與結束位址是否合法
- 如果合法,給定
mem_size
為結束位址 - 起始位址 - 2 倍的struct mem
大小 - 也就是與
mem_heap
相同,一開始的記憶體設定為一大塊,頭與尾都要有一個 header
212 | /* point to begin address of heap */ |
- 接著設定前面的 header:
- 設定 magic 碼
- 下一塊為結尾的 header
- 上一塊為自己
- 以及沒有使用過
224 |
|
- 接著設定結尾的 header
- magic 碼
- 已被使用過
- 上一塊與下一塊指向自己
237 | rt_sem_init(&heap_sem, "heap", 1, RT_IPC_FLAG_FIFO); |
- 最後初始化 semaphore
- 把這一塊掛上
lfree
分配記憶體#
Code: rt_malloc#
功能 | 回傳值 | size |
---|---|---|
要求記憶體 | 記憶體位址 | 欲要求的大小 |
252 | /** |
- 如果
size
為 0,回傳 NULL
266 | RT_DEBUG_NOT_IN_INTERRUPT; |
- 向上對齊
size
,如果超過可用大小,回傳 NULL
283 | /* every data block must be at least MIN_SIZE_ALIGNED long */ |
- 如果小於 min size,設為 min size
286 | /* take memory semaphore */ |
- 取得 semaphore
288 | for (ptr = (rt_uint8_t *)lfree - heap_ptr; |
這裡特別的說明一下 for 迴圈:
首先起點是 lfree
- heap_ptr
,這裡代表最左邊的 free block 與 heap 起點的距離。 我們把 heap_ptr
看成是一個 rt_uint8_t
的陣列,也就是一格一個 byte 的陣列。 再來把 lfree
- heap_ptr
看成是差量 (offset),單位是 byte。 如此一來,&heap_ptr[ptr]
就會是 lfree
的起始位置了。
再來我們看 next
,在初始化的時候,next
是指向 0,這個意思是下一顆在陣列的第 0 個,也就是自己;所以 next
存放的是下一顆的 index,而不是起始位置。
最後來看上界,理論上我們需要從 lfree 找到最後一顆,實際上如果最後幾顆不夠大的話是不需要檢查的,所以這裡上界設在 mem_size_aligned
- size
的意思就是說如果最後幾顆的大小總和不夠大,我們可以略過。
293 | if ((!mem->used) && (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) |
- first fit,如果找到第一顆可用的就進去
297 | if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= |
- 又,如果這顆夠大到可以切割的話
300 | /* (in addition to the above, we test if another struct heap_mem (SIZEOF_STRUCT_MEM) containing |
- 設定下一顆的資料,同時把
next
與prev
接到正確位置
321 | /* and insert it between mem and mem->next */ |
- 接著把原本那塊的
next
指向新的那塊,設為使用中 - 如果新的那塊
next
不是最後一塊,設定prev
329 |
|
- 最後更新
used_mem
與max_mem
335 | else |
- 如果不可切割,只需設定使用中即可
351 | /* set memory block magic */ |
- 視情況更新
lfree
368 | rt_sem_release(&heap_sem); |
- 還鎖,並回傳找到的記憶體位址
385 | rt_sem_release(&heap_sem); |
- 沒找到一樣還鎖,並回傳 NULL
Code: rt_realloc#
功能 | 回傳值 |
---|---|
增長/縮減記憶體 | 記憶體位址 |
*rmeme |
newsize |
---|---|
欲增長/縮減的記憶體位址 | 新的大小 |
400 | /** |
- 向上對齊 size,如果:
- 大於可用大小,回傳 NULL
- 等於 0,free 記憶體,回傳 NULL
430 | /* allocate a new memory block */ |
- 如原來的記憶體為空,直接
malloc
,並回傳
433 | rt_sem_take(&heap_sem, RT_WAITING_FOREVER); |
- 接著取得鎖,檢查傳入的記憶體是否合法
443 | mem = (struct heap_mem *)((rt_uint8_t *)rmem - SIZEOF_STRUCT_MEM); |
- 找到記憶體塊的起始位址,算出 size,如果記憶體大小不需要變動,不做事,回傳原本的記憶體位址
454 | if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE < size) |
- 如果可以切割,與上面的動作相同
475 | plug_holes(mem2); |
- 使用
plug_holes
來合併 free block - 還鎖,回傳更新後的記憶體位置
481 | rt_sem_release(&heap_sem); |
- 如果不可切割,或是需要增長,直接要一塊 new size,釋放原本的記憶體
- 最後回傳新的記憶體位址
功能 | 回傳值 | *mem |
---|---|---|
合併 free block | void | 欲合併的記憶體位址 |
143 | static void plug_holes(struct heap_mem *mem) |
- 如果可以與下一顆合併
- 檢查是否需要更新
lfree
- 重新接上
next
與prev
(next
的prev
)
168 | /* plug hole backward */ |
- 如果可以與上一顆合併,動作一樣
Code: rt_calloc#
功能 | 回傳值 |
---|---|
要求一段連續的記憶體 | 記憶體位址 |
count |
size |
---|---|
欲要求的數量 | 一塊的大小 |
500 | /** |
- 與 memheap 相同,一次要一塊 count 乘 size 的記憶體
- 清 0 並回傳起始位址
釋放記憶體#
功能 | 回傳值 | *rmem |
---|---|---|
釋放記憶體 | void | 欲釋放的記憶體 |
527 | /** |
- 如果需要釋放得記憶體為空,不做事
539 | RT_DEBUG_NOT_IN_INTERRUPT; |
- 檢查記憶體位址是否合法,並找到真正的記憶體區塊位址
557 | RT_DEBUG_LOG(RT_DEBUG_MEM, |
- 要鎖,檢查是否是使用中的區塊,及是否屬於 heap
574 | /* ... and is now unused. */ |
- 接著設為可使用,及更新
lfree
586 |
|
- 最後合併記憶體塊,並還鎖