Threadx 提供字節內存池進行內存管理,字節內存池是一塊連續字節塊,可以以字節爲單位申請內存。
字節內存池中連續內存初始時有兩個字節塊,這兩塊通過單向循環鏈表連接。隨着內存申請或釋放整個大的連續內存被分爲更多的字節塊,這些字節塊通過單向鏈表連接。內存分配使用首次適應(fisrt-fit)算法。
內存池控制塊
TX_BYTE_POOL結構體用來描述字節池管理結構。
/* Define the byte memory pool structure utilized by the application. */
typedef struct TX_BYTE_POOL_STRUCT
{
/* Define the byte pool ID used for error checking. */
ULONG tx_byte_pool_id;
/* Define the byte pool's name. */
CHAR_PTR tx_byte_pool_name;
/* Define the number of available bytes in the pool. */
ULONG tx_byte_pool_available;
/* Define the number of fragments in the pool. */
ULONG tx_byte_pool_fragments;
/* Define the head pointer of byte pool. */
CHAR_PTR tx_byte_pool_list;
/* Define the search pointer used for initial searching for memory
in a byte pool. */
CHAR_PTR tx_byte_pool_search;
/* Save the start address of the byte pool's memory area. */
CHAR_PTR tx_byte_pool_start;
/* Save the byte pool's size in bytes. */
ULONG tx_byte_pool_size;
/* This is used to mark the owner of the byte memory pool during
a search. If this value changes during the search, the local search
pointer must be reset. */
struct TX_THREAD_STRUCT *tx_byte_pool_owner;
/* Define the byte pool suspension list head along with a count of
how many threads are suspended. */
struct TX_THREAD_STRUCT *tx_byte_pool_suspension_list;
ULONG tx_byte_pool_suspended_count;
/* Define the created list next and previous pointers. */
struct TX_BYTE_POOL_STRUCT
*tx_byte_pool_created_next,
*tx_byte_pool_created_previous;
} TX_BYTE_POOL;
域 | 意義 |
---|---|
tx_byte_pool_id | 字節內存池id |
tx_byte_pool_name | 字節內存池名字 |
tx_byte_pool_available | 字節內存池可分配字節 |
tx_byte_pool_fragments | 字節內存池塊個數 |
tx_byte_pool_list | 字節內存池頭list指針 |
tx_byte_pool_search | 字節內存池開始搜索起始地址 |
tx_byte_pool_start | 字節內存池所在內存空間起始地址 |
tx_byte_pool_size | 字節內存池大小 |
tx_byte_pool_owner | 字節內存池所屬線程 |
tx_byte_pool_suspension_list | 掛起線程鏈表 |
tx_byte_pool_created_next | 指向下一個字節內存池指針 |
tx_byte_pool_created_previous | 指向前一個字節內存池指針 |
內存池鏈表
系統中所有創建的字節內存池都插入到鏈表_tx_byte_pool_created_ptr。tx_byte_pool_created_next指向下一個字節內存池指針,tx_byte_pool_created_previous指向前一個字節內存池指針。
內存池初始化
應用開發者指定字節內存池起始地址和大小,調用_tx_byte_pool_create創建內存池。
內存池初始化爲一個大的塊和一個小的控制塊兩部分,小的控制塊只是爲了fisrt-fit算法,不會分配給應用使用。
內存池中不同塊(block)之間通過單向鏈表連接,但沒有專門定義鏈表指針,而是利用每塊內存頭部前8個字節作爲控制字段,前4個字節指向下一個內存塊的起始地址,隨後4個字節作爲內存是否分配標誌,如果已經被分配,指向內存池TX_BYTE_POOL結構指針,如果空閒設置爲TX_BYTE_BLOCK_FREE。
最後一塊內存池只有8個字節,作爲控制塊,前4個字節指向內存池TX_BYTE_POOL結構指針,後4個字節設置爲TX_BYTE_BLOCK_ALLOC。
#define TX_BYTE_BLOCK_ALLOC 0xAAAAAAAAUL
#define TX_BYTE_BLOCK_FREE 0xFFFFEEEEUL
如下圖,pool_ptr指向TX_BYTE_POOL內存池管理結構,整個內存分爲兩塊,TX_BYTE_POOL中tx_byte_pool_start指向內存池首地址。第一塊中的前4個字節存儲第二塊(最後一塊,控制塊)的起始地址,後四個字節設置爲TX_BYTE_BLOCK_FREE,因爲還沒有使用,標記爲空閒塊。
第二塊(最後一塊,控制塊)的前4個字節存放指向內存池起始地址tx_byte_pool_start,後4字節標記爲TX_BYTE_BLOCK_ALLOC 標記爲已分配,也表示最後一塊。
tx_byte_pool_search指向搜索內存時的開始地址。
這樣內存塊就構成了單向循環鏈表。
內存分配
_tx_byte_allocate函數用來內存分配。
內存分配使用首次適應(fisrt-fit)算法,從上次操作的空閒內存塊開始查找,找到大小合適內存,返回成功。
查找到第一個大於請求分配大小的內存塊時,就認爲找到了,把這個大的內存塊分爲兩個內存塊,前一個返回應用程序使用,後一個內存塊掛入內存管理鏈表中。
初始化時,內存塊只有兩個,隨着分配內存和釋放內存,會出現很多小的內存塊,稱爲內存碎片。
查找過程中,發現兩個鄰居內存塊是地址連續的,那麼把這兩個內存塊合併成爲一個內存塊,稱爲內存整理。
如果內存池沒有足夠可用內存,申請分配內存的線程會掛起。
舉例:
下圖爲第一次分配內存後,最初的第一塊內存被分爲了兩塊:第一塊和第二塊。
第一塊返回給應用程序,第一塊前面8個字節由於是控制字段,所以返回應用程序的內存起始地址跨過了前面8個字節,memptr指向分配的地址。第一塊的前4字節存儲第二塊內存起始地址,隨後4個字節指向了內存池管理結構,標誌着第一塊內存已經被佔用,不是空閒內存了。
第二塊內存前4個字節指向第三塊內存(最後一塊內存),繼續構成單向鏈表。隨後4個字節存儲TX_BYTE_BLOCK_FREE,表示還是空閒內存塊。
tx_byte_pool_search指向搜索內存時的開始地址,第一塊已經被佔用,所以指向了第一個空閒的內存塊,即第二塊內存起始地址。
如下圖,第二次分配內存。
從上圖中第一個空閒的內存塊(第二塊)開始搜索,找到了合適大小,由於請求大小小於空閒的內存塊大小,所以把空閒塊再次分爲兩塊,如下圖所示。
第二塊是分配給應用程序的,memptr爲返回地址,第二塊內存的前4字節指向第三塊內存首地址,隨後4個字節指向了內存池管理結構,表示這塊內存已經被佔用。
第三塊內存,前4字節指向第四塊內存(最後一塊內存),繼續構成單向鏈表。隨後4個字節存儲TX_BYTE_BLOCK_FREE,表示還是空閒內存塊。
內存釋放
_tx_byte_release函數用來內存釋放。
內存釋放時,根據釋放內存首地址,減8,計算出內存塊控制字段的首地址,通過前四字節找到內存池管理結構指針,可以進行內存釋放操作。
釋放後,4到8字節存儲TX_BYTE_BLOCK_FREE表示爲空閒內存塊。
這裏第二塊內存和第三塊內存雖然是連續的,但並沒有進行合併或內存整理。什麼時間合併呢?
等到申請內存分配,發現請求大小大於第二塊的大小,開始查找第三塊時,這時發現第二塊和第三塊內存連續,就把第二塊和第三塊合併爲一塊。
釋放內存後,會檢查掛起鏈表中是否有線程,如果有嘗試分配內存並恢復線程執行。線程恢復是按照FIFO順序恢復,並沒有按照線程優先級高低順序。但是可以在線程釋放前調用_tx_byte_pool_prioritize,把最高優先級線程移動到掛起鏈表最前面,從而先恢復最高優先級線程。
內存整理
內存在多次分配和釋放後,可能會出現大量小的內存塊,這種想象成爲內存碎片化。
當需要分配一個較大內存時,每次可能需要先遍歷大量小內存,這樣會使查找開銷增加,算法性能下降。由於每個內存塊都佔用8個字節的控制字段,大量小內存會導致內存的浪費。
查找過程中,發現兩個鄰居內存塊是地址連續的,那麼把這兩個內存塊合併成爲一個內存塊,稱爲內存整理。內存整理能夠提升算法性能,但提升有限。其它優化方式,可以從查找算法進行優化,如二叉樹查找等。
內存整理舉例:
假設下圖爲多次內存分配和釋放後的結構,第一塊被佔用,第二塊,第三塊,第四塊爲空閒內存塊。
第二塊大小爲64字節,第三塊大小爲64字節,第四塊內存大小爲256字節。
假如現在申請分配內存大小爲128字節,在分配查找過程中,發現第二塊太小不滿足,但第二塊和第三塊地址連續,於是把第二塊和第三塊合併爲一塊,並且發現合併後正好滿足請求,返回給應用程序,如下面第二個圖。
字節池內存API
函數 | 描述 |
---|---|
tx_byte_pool_create | 創建內存字節池 |
tx_byte_pool_delete | 刪除內存字節池 |
tx_byte_allocate | 分配內存字節 |
tx_byte_release | 釋放內存 |
_tx_byte_pool_info_get | 獲得內存池信息 |
_tx_byte_pool_prioritize | 調整內存池掛起鏈表最前面爲最高優先級線程 |
小結
字節內存池使用比較靈活,但也會出現內存碎片問題,導致搜索開銷大,系統性能下降,特別是由於小內存多導致線程申請不到足夠內存自我掛起,將嚴重影響系統實時性。
實時系統中,應用程序通常會根據應用使用內存空間,單獨創建內存管理算法,通過測試,保證永遠不會出現內存不夠掛起問題。
_tx_byte_pool_create
字節內存池創建,初始化內存池,把內存池插入_tx_byte_pool_created_ptr鏈表。
pool_start爲內存池的起始地址,pool_size爲內存池大小
UINT _tx_byte_pool_create(TX_BYTE_POOL *pool_ptr, CHAR *name_ptr, VOID *pool_start,
ULONG pool_size)
{
TX_INTERRUPT_SAVE_AREA
TX_BYTE_POOL *tail_ptr; /* Working byte pool pointer */
CHAR_PTR block_ptr; /* Working block pointer */
/* Round the pool size down to something that is evenly divisible by
an ULONG. */
#def 內存池大小 sizeof(ULONG)個字節對齊
pool_size = (pool_size / sizeof(ULONG)) * sizeof(ULONG);
/* Setup the basic byte pool fields. */
#def 初始化參數
pool_ptr -> tx_byte_pool_name = name_ptr;
pool_ptr -> tx_byte_pool_suspension_list = TX_NULL;
pool_ptr -> tx_byte_pool_suspended_count = 0;
/* Save the start and size of the pool. */
#def 保存內存池起始地址和內存大小
pool_ptr -> tx_byte_pool_start = (CHAR_PTR) pool_start;
pool_ptr -> tx_byte_pool_size = pool_size;
/* Setup memory list to the beginning as well as the search pointer. */
#def 設置內存鏈表首地址,內存池搜索起始地址
pool_ptr -> tx_byte_pool_list = (CHAR_PTR) pool_start;
pool_ptr -> tx_byte_pool_search = (CHAR_PTR) pool_start;
/* Initially, the pool will have two blocks. One large block at the
beginning that is available and a small allocated block at the end
of the pool that is there just for the algorithm. Be sure to count
the available block's header in the available bytes count. */
#def 設置可用內存大小,總的大小減去控制字段
pool_ptr -> tx_byte_pool_available = pool_size - sizeof(VOID_PTR) - sizeof(ULONG);
#def 內存池最開始初始化爲兩塊,一個大的塊用於分配內存;一個小的固定塊,僅用於內存分配算法計算
pool_ptr -> tx_byte_pool_fragments = 2;
/* Calculate the end of the pool's memory area. */
#def 指向內存池最後地址
block_ptr = ((CHAR_PTR) pool_start) + (UINT) pool_size;
/* Backup the end of the pool pointer and build the pre-allocated block. */
#def 內存池最後一塊,最後4字節存儲TX_BYTE_BLOCK_ALLOC
block_ptr = block_ptr - sizeof(ULONG);
*((ULONG_PTR) block_ptr) = TX_BYTE_BLOCK_ALLOC;
#def 內存池最後一塊,前面4字節存儲pool_start內存池起始地址
block_ptr = block_ptr - sizeof(CHAR_PTR);
*((CHAR_PTR *) block_ptr) = pool_start;
/* Now setup the large available block in the pool. */
#def 內存池第1塊,最前面4字節存儲第二塊內存的首地址
*((CHAR_PTR *) pool_start) = block_ptr;
block_ptr = (CHAR_PTR) pool_start;
block_ptr = block_ptr + sizeof(CHAR_PTR);
#def 隨後4字節存儲TX_BYTE_BLOCK_FREE,指示爲空閒塊
*((ULONG_PTR) block_ptr) = TX_BYTE_BLOCK_FREE;
/* Clear the owner id. */
pool_ptr -> tx_byte_pool_owner = TX_NULL;
/* Disable interrupts to place the byte pool on the created list. */
TX_DISABLE
/* Setup the byte pool ID to make it valid. */
#def 內存池有效
pool_ptr -> tx_byte_pool_id = TX_BYTE_POOL_ID;
/* Place the byte pool on the list of created byte pools. First,
check for an empty list. */
#def 把內存池插入_tx_byte_pool_created_ptr list
if (_tx_byte_pool_created_ptr)
{
/* Pickup tail pointer. */
tail_ptr = _tx_byte_pool_created_ptr -> tx_byte_pool_created_previous;
/* Place the new byte pool in the list. */
_tx_byte_pool_created_ptr -> tx_byte_pool_created_previous = pool_ptr;
tail_ptr -> tx_byte_pool_created_next = pool_ptr;
/* Setup this byte pool's created links. */
pool_ptr -> tx_byte_pool_created_previous = tail_ptr;
pool_ptr -> tx_byte_pool_created_next = _tx_byte_pool_created_ptr;
}
else
{
/* The created byte pool list is empty. Add byte pool to empty list. */
_tx_byte_pool_created_ptr = pool_ptr;
pool_ptr -> tx_byte_pool_created_next = pool_ptr;
pool_ptr -> tx_byte_pool_created_previous = pool_ptr;
}
/* Increase the byte pool created count. */
_tx_byte_pool_created_count++;
/* Restore interrupts. */
TX_RESTORE
/* Return TX_SUCCESS. */
return (TX_SUCCESS);
}
_tx_byte_pool_delete
把內存池從_tx_byte_pool_created_ptr鏈表刪除,恢復掛起鏈表中的線程。
UINT _tx_byte_pool_delete(TX_BYTE_POOL *pool_ptr)
{
TX_INTERRUPT_SAVE_AREA
TX_THREAD *thread_ptr; /* Working thread pointer */
/* Disable interrupts to remove the byte pool from the created list. */
TX_DISABLE
/* Decrease byte pool created count. */
_tx_byte_pool_created_count--;
/* Clear the byte pool ID to make it invalid. */
#def 設置爲無效
pool_ptr -> tx_byte_pool_id = 0;
/* See if the byte pool is the only one on the list. */
#def 從_tx_byte_pool_created_ptr 鏈表刪除內存
if (pool_ptr == pool_ptr -> tx_byte_pool_created_next)
{
/* Only created byte pool, just set the created list to NULL. */
_tx_byte_pool_created_ptr = TX_NULL;
}
else
{
/* Link-up the neighbors. */
(pool_ptr -> tx_byte_pool_created_next) -> tx_byte_pool_created_previous =
pool_ptr -> tx_byte_pool_created_previous;
(pool_ptr -> tx_byte_pool_created_previous) -> tx_byte_pool_created_next =
pool_ptr -> tx_byte_pool_created_next;
/* See if we have to update the created list head pointer. */
if (_tx_byte_pool_created_ptr == pool_ptr)
/* Yes, move the head pointer to the next link. */
_tx_byte_pool_created_ptr = pool_ptr -> tx_byte_pool_created_next;
}
/* Temporarily disable preemption. */
_tx_thread_preempt_disable++;
/* Restore interrupts. */
TX_RESTORE
/* Walk through the byte pool list to resume any and all threads suspended
on this byte pool. */
thread_ptr = pool_ptr -> tx_byte_pool_suspension_list;
#def 遍歷掛起鏈表,恢復線程
while (pool_ptr -> tx_byte_pool_suspended_count)
{
/* Lockout interrupts. */
TX_DISABLE
/* Clear the cleanup pointer, this prevents the timeout from doing
anything. */
thread_ptr -> tx_suspend_cleanup = TX_NULL;
/* Temporarily disable preemption again. */
_tx_thread_preempt_disable++;
/* Restore interrupts. */
TX_RESTORE
/* Yes, deactivate the thread's timer just in case. */
#def 關閉定時器
_tx_timer_deactivate(&(thread_ptr -> tx_thread_timer));
/* Clear the remaining time to ensure timer doesn't get activated. */
thread_ptr -> tx_thread_timer.tx_remaining_ticks = 0;
/* Set the return status in the thread to TX_DELETED. */
thread_ptr -> tx_suspend_status = TX_DELETED;
/* Move the thread pointer ahead. */
thread_ptr = thread_ptr -> tx_suspended_next;
/* Resume the thread. */
#def 恢復線程
_tx_thread_resume(thread_ptr -> tx_suspended_previous);
/* Decrease the suspended count. */
pool_ptr -> tx_byte_pool_suspended_count--;
}
/* Disable interrupts. */
TX_DISABLE
/* Release previous preempt disable. */
_tx_thread_preempt_disable--;
/* Restore interrupts. */
TX_RESTORE
/* Check for preemption. */
#def 恢復線程後,可能有高優先級線程,切換調度
if (_tx_thread_current_ptr != _tx_thread_execute_ptr)
/* Transfer control to system. */
_tx_thread_system_return();
/* Return TX_SUCCESS. */
return (TX_SUCCESS);
}