使用此管理方式:
#define RT_USING_MEMHEAP_AS_HEAP
- memheap 的管理方法(動態管理):
- 從 RAM 中要一塊記憶體
- 根據使用者需要的大小進行切割
- 剩下的以雙向鏈結的方式接起來,形成 free list
結構#
File: rtdef.h
698 |
|
*start_addr
指向可用的記憶體pool_size
代表總共可用的大小available_size
目前可用的大小max_used_size
已使用的歷史中,最大的使用大小*block_list
所有切割過的區塊(包含 header)*free_list
目前所有可用的區塊*free_list
的第一顆lock
semaphore
File: memheap.c
建立 memory heap#
功能 | 回傳值 |
---|---|
建立 memheap | void |
*begin_addr |
*end_addr |
---|---|
起始位址(欲分配的) | 結束位址 |
601 | void rt_system_heap_init(void *begin_addr, void *end_addr) |
- 將起始位置,大小,結構體傳入
rt_memheap_init
功能 | 回傳值 |
---|---|
初始化 memheap | RT_EOK |
*memheap |
*name |
*start_addr |
size |
---|---|---|---|
memheap 結構 | 名字 | 欲分配的記憶體起始位址 | 記憶體大小 |
39 | /* |
- 首先填入
start_addr
- 向下對齊
size
- 設定可用大小為
size
減掉 2 個 header - 設定最大已使用大小為目前已使用的大小(即 2 倍的 header)
66 | /* initialize the free list header */ |
- 先初始化 free list:
- 讓 item 指向 free list 的 header
- 設定 magic 碼
- 將
pool_ptr
指向 memheap 結構 next
、prev
指向NULL
next_free
、prev_free
指向自己
74 | /* set the free list to free list header */ |
- 給定 free list
76 | /* initialize the first big memory block */ |
- 接著將整個 pool 設定為一個可用的 block
- 讓 item 指向 起始位址
- 設定 magic 碼
- 將
pool_ptr
指向 memheap 結構 next
、prev
指向NULL
next_free
、prev_free
指向自己
84 | item->next = (struct rt_memheap_item *) |
- 讓 next 與 prev 指到結尾的 header
87 | /* block list header */ |
- 給定 block_list
89 | /* place the big memory block to free list */ |
- 將 free list (item) 的
next
指向memheap->free_list->next_free
,也就是 free list prev
同上- 將 free list (heap) 的
next
指向item
prev
同上
94 | /* move to the end of memory pool to build a small tailer block, |
- 設定尾巴的 header
- 讓 item 指向 free list 的 header
- 設定 magic 碼為使用過的
- 將
pool_ptr
指向 memheap 結構 next
、prev
指向起始位置next_free
、prev_free
指向NULL
105 | /* initialize semaphore lock */ |
- 最後初始化 semaphore 並使用 FIFO
刪除 memory heap#
功能 | 回傳值 | *heap |
---|---|---|
刪除 memheap | RT_EOK |
欲刪除的 memheap |
124 | rt_err_t rt_memheap_detach(struct rt_memheap *heap) |
- 使用
rt_object_detach
刪除 semaphore 與 memheap
分配記憶體#
Code: rt_malloc#
功能 | 回傳值 | size |
---|---|---|
要求一塊記憶體 | 取得的記憶體 | 欲要求的大小 |
610 | void *rt_malloc(rt_size_t size) |
- 首先嘗試從系統的 heap(
_heap
)要求記憶體(透過rt_memheap_alloc
)
616 | if (ptr == RT_NULL) |
- 如果失敗,嘗試從其他的 heap 要求
625 | RT_ASSERT(information != RT_NULL); |
- 跳過系統的 heap
639 | ptr = rt_memheap_alloc(heap, size); |
- 一樣透過
rt_memheap_alloc
來完成 - 如果成功就跳出迴圈,最後回傳記憶體位址
功能 | 回傳值 |
---|---|
要求一塊記憶體 | 取得的記憶體 |
*heap |
size |
---|---|
目標 heap | 欲要求的大小 |
138 | void *rt_memheap_alloc(struct rt_memheap *heap, rt_uint32_t size) |
- 首先向上對齊
size
- 如果小於
RT_MEMHEAP_MINIALLOC
(12),設定為RT_MEMHEAP_MINIALLOC
151 | RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("allocate %d on heap:%8.*s", |
- 如果 heap 還夠使用,先將
free_size
設為 0 free_size
代表我們目前找到的可用大小
158 | /* lock memheap */ |
- 接著試著索取 semaphore
- 如果失敗,設定錯誤碼並回傳 NULL
166 | /* get the first free memory block */ |
- 接著從 free list 上一個一個找
- 使用 first fit,找到一個大魚的就退出迴圈
MEMITEM_SIZE(item)
:((rt_uint32_t)item->next - (rt_uint32_t)item - RT_MEMHEAP_SIZE)
- 利用下一顆的位址減掉自己的位址取的總體大小,再減掉 header 的大小
178 | /* determine if the memory is available. */ |
- 如果有成功找到(不是因為走完迴圈才往下)
- 且這塊大到可以再切一塊,切割這塊:
- 從找到的那塊開始往後一個
size
與一個RT_MEMHEAP_SIZE
作為新的 header
- 從找到的那塊開始往後一個
191 | RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, |
- 設定 magic 碼
- 設定所屬 heap
203 | /* break down the block list */ |
- 將此 block 插入
block_list
208 | /* remove header ptr from free list */ |
- 從 free list 中移除找到的 block
213 | /* insert new_ptr to free list */ |
- 將分割好的 block 插入 free list
221 | /* decrement the available byte count. */ |
- 更新
available_size
與max_used_size
(如果需要)
228 | else |
- 如果不能切割,一樣更新
available_size
與max_used_size
(如果需要)
234 | /* remove header_ptr from free list */ |
- 從 free list 中移除找到的 block
246 | /* Mark the allocated block as not available. */ |
- 標記為使用中,釋放 semaphore
251 | /* Return a memory address to the caller. */ |
- 最後回傳 block 記憶體位址 + header
- 即回傳可用的區塊
260 | /* release lock */ |
- 如果找失敗,一樣釋放 semaphore
- 不論是找失敗,或是記憶體不足,皆回傳 NULL
Code: rt_realloc#
功能 | 回傳值 |
---|---|
重新要求記憶體(增長或縮減) | 新分配完的記憶體塊 |
*rmem |
newsize |
---|---|
欲重新分配的記憶體 | 新的大小 |
656 | void *rt_realloc(void *rmem, rt_size_t newsize) |
- 如果傳入的記憶體位置為空,直接
rt_malloc(newsize)
並回傳
663 | if (newsize == 0) |
- 如果
newsize
為 0,free 傳入的記憶體位置,回傳 NULL
668 | /* get old memory item */ |
- 取得傳入的記憶體塊所屬的 header
- malloc 時回傳的是可使用的起始位址,並不會包含 header,因此這裡減掉一個 header 的大小
671 | new_ptr = rt_memheap_realloc(header_ptr->pool_ptr, rmem, newsize); |
- 透過
rt_memheap_realloc
來完成
672 | if (new_ptr == RT_NULL && newsize != 0) |
- 如果無法在原本的 heap 完成增長(或縮減),直接從別的 heap 要一塊
newsize
大的記憶體
676 | if (new_ptr != RT_NULL && rmem != RT_NULL) |
- 如果最後有要成功,復原原本的資料
690 | return new_ptr; |
- 最後回傳新的記憶體位址
功能 | 回傳值 |
---|---|
重新要求記憶體(增長或縮減) | 新分配完的記憶體塊 |
heap |
*ptr |
newsize |
---|---|---|
目標 heap | 欲重新分配的記憶體 | 新的大小 |
284 | void *rt_memheap_realloc(struct rt_memheap *heap, void *ptr, rt_size_t newsize) |
- 如果
newsize
為 0,free 並回傳 NULL
300 | /* align allocated size */ |
- 向上對齊
newsize
- 如果小於
RT_MEMHEAP_MINIALLOC
(12),設定為RT_MEMHEAP_MINIALLOC
304 | if (ptr == RT_NULL) |
- 如果傳入的記憶體位置為空,直接 malloc newsize 的大小並回傳
308 | /* get memory block header and get the size of memory block */ |
- 取得傳入的 block 所屬的 header
- 一併計算這塊的大小
312 | /* re-allocate memory */ |
- 如果需要增長記憶體,先取得 semaphore
325 | next_ptr = header_ptr->next; |
- 先判斷下一顆可不可用
337 | /* Here is the ASCII art of the situation that we can make use of |
- 如果可用,而且下一顆足夠分割出一塊新的 block
- 更新
available_size
與max_used_size
(如果需要)
352 | /* remove next_ptr from free list */ |
- 從 free list 移除舊的下一顆
363 | /* build a new one on the right place */ |
- 重新定指新的下一顆(傳入的起始位址加上
newsize
)
365 | RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, |
- 設定 magic 碼
- 設定所屬 heap
376 | next_ptr->prev = header_ptr; |
- 插入 block list
380 | /* insert next_ptr to free list */ |
插入 free list
388 | /* release lock */ |
- 釋放 semaphore 並回傳更新後的記憶體位址
394 | /* release lock */ |
- 如果下一顆不夠大,重新在原本的 heap 上要一塊
newsize
大的記憶體 - 成功的話還原資料,並釋放原本的記憶體
- 回傳新的記憶體位址
407 | /* don't split when there is less than one node space left */ |
- 如果是需要縮減,且縮減後剩下的大小不足以切成一塊
- 什麼事都不做,直接回傳原本的位址
410 | /* lock memheap */ |
- 可以分割的話先取得 semaphore
418 | /* split the block. */ |
- 定址新的 block
421 | RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, |
- 設定 magic 碼
- 設定所屬 heap
432 | /* break down the block list */ |
- 插入至 block list
437 | /* determine if the block can be merged with the next neighbor. */ |
- 如果新的 block 下一顆未使用,即可合併
- 先將可用大小減掉下一顆的大小,待會會加回來
445 | RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, |
- 從 block list 移除下一顆
451 | /* remove free ptr from free list */ |
- 從 free list 移除下一顆,完成合併
455 | /* insert the split block to free list */ |
- 無論下一顆是否可以合併,都把新的 block 插入 free list
463 | /* increment the available byte count. */ |
- 更新可用大小,並釋放 semaphore
- 回傳更新後的記憶體位址
Code: rt_calloc#
功能 | 回傳值 |
---|---|
要求多個連續的記憶體 | 第一塊的位址 |
count |
size |
---|---|
欲要求的數量 | 欲要求的大小 |
698 | void *rt_calloc(rt_size_t count, rt_size_t size) |
- 即要求一塊
count * size
大的記憶體
釋放記憶體#
功能 | 回傳值 | *rmem |
---|---|---|
釋放一塊記憶體 | void | 欲釋放的記憶體 |
650 | void rt_free(void *rmem) |
- 透過
rt_memheap_free
完成
功能 | 回傳值 | *ptr |
---|---|---|
釋放一塊記憶體 | void | 欲釋放的記憶體 |
495 | void rt_memheap_free(void *ptr) |
- 如果傳入 NULL,什麼事都不用做
return
退出副程式
504 | /* set initial status as OK */ |
- 初始化一些參數,並找到傳入的 block 所屬的 header
509 | RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("free memory: memory[0x%08x], block[0x%08x]\n", |
- 定址 heap
520 | RT_ASSERT(heap); |
- 先取得 semaphore
531 | /* Mark the memory as available. */ |
- 將使用中的標記清除,更新可用大小
535 | /* Determine if the block can be merged with the previous neighbor. */ |
- 如果可以往前合併,更新可用大小(加一個 header 的大小)
543 | /* yes, merge block with previous neighbor. */ |
- 從 block list 移除此 block
546 | /* move header pointer to previous. */ |
- 重新定址
header_ptr
- 設定
insert_header
為 0,表示待會不需要將此 block 插回 free list(現在此 block 是與前一塊合併的,已經在 free list 上了)
551 | /* determine if the block can be merged with the next neighbor. */ |
- 如果可以往前合併,更新可用大小(加一個 header 的大小)
556 | /* merge block with next neighbor. */ |
- 定址下一塊,並從 block list 移除下一塊
565 | /* remove new ptr from free list */ |
- 一併從 free list 中移除
569 | if (insert_header) |
- 如果需要,插回 free list 上
581 | /* release lock */ |
- 最後釋放 semaphore