Threadx 內存管理-內存字節池


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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章