Threadx 内存管理-内存字节池

文章目录

    • 内存池控制块
    • 内存池链表
    • 内存池初始化
    • 内存分配
    • 内存释放
    • 内存整理
    • 字节池内存API
    • 小结
    • _tx_byte_pool_create
    • _tx_byte_pool_delete

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指向前一个字节内存池指针。

Threadx 内存管理-内存字节池_第1张图片

内存池初始化

应用开发者指定字节内存池起始地址和大小,调用_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指向搜索内存时的开始地址。
这样内存块就构成了单向循环链表。

Threadx 内存管理-内存字节池_第2张图片

内存分配

_tx_byte_allocate函数用来内存分配。
内存分配使用首次适应(fisrt-fit)算法,从上次操作的空闲内存块开始查找,找到大小合适内存,返回成功。
查找到第一个大于请求分配大小的内存块时,就认为找到了,把这个大的内存块分为两个内存块,前一个返回应用程序使用,后一个内存块挂入内存管理链表中。
初始化时,内存块只有两个,随着分配内存和释放内存,会出现很多小的内存块,称为内存碎片。
查找过程中,发现两个邻居内存块是地址连续的,那么把这两个内存块合并成为一个内存块,称为内存整理。
如果内存池没有足够可用内存,申请分配内存的线程会挂起。

举例:
下图为第一次分配内存后,最初的第一块内存被分为了两块:第一块和第二块。
第一块返回给应用程序,第一块前面8个字节由于是控制字段,所以返回应用程序的内存起始地址跨过了前面8个字节,memptr指向分配的地址。第一块的前4字节存储第二块内存起始地址,随后4个字节指向了内存池管理结构,标志着第一块内存已经被占用,不是空闲内存了。
第二块内存前4个字节指向第三块内存(最后一块内存),继续构成单向链表。随后4个字节存储TX_BYTE_BLOCK_FREE,表示还是空闲内存块。
tx_byte_pool_search指向搜索内存时的开始地址,第一块已经被占用,所以指向了第一个空闲的内存块,即第二块内存起始地址。

Threadx 内存管理-内存字节池_第3张图片
如下图,第二次分配内存。
从上图中第一个空闲的内存块(第二块)开始搜索,找到了合适大小,由于请求大小小于空闲的内存块大小,所以把空闲块再次分为两块,如下图所示。
第二块是分配给应用程序的,memptr为返回地址,第二块内存的前4字节指向第三块内存首地址,随后4个字节指向了内存池管理结构,表示这块内存已经被占用。
第三块内存,前4字节指向第四块内存(最后一块内存),继续构成单向链表。随后4个字节存储TX_BYTE_BLOCK_FREE,表示还是空闲内存块。

Threadx 内存管理-内存字节池_第4张图片

内存释放

_tx_byte_release函数用来内存释放。
内存释放时,根据释放内存首地址,减8,计算出内存块控制字段的首地址,通过前四字节找到内存池管理结构指针,可以进行内存释放操作。
释放后,4到8字节存储TX_BYTE_BLOCK_FREE表示为空闲内存块。
这里第二块内存和第三块内存虽然是连续的,但并没有进行合并或内存整理。什么时间合并呢?
等到申请内存分配,发现请求大小大于第二块的大小,开始查找第三块时,这时发现第二块和第三块内存连续,就把第二块和第三块合并为一块。
释放内存后,会检查挂起链表中是否有线程,如果有尝试分配内存并恢复线程执行。线程恢复是按照FIFO顺序恢复,并没有按照线程优先级高低顺序。但是可以在线程释放前调用_tx_byte_pool_prioritize,把最高优先级线程移动到挂起链表最前面,从而先恢复最高优先级线程。

Threadx 内存管理-内存字节池_第5张图片

内存整理

内存在多次分配和释放后,可能会出现大量小的内存块,这种想象成为内存碎片化。
当需要分配一个较大内存时,每次可能需要先遍历大量小内存,这样会使查找开销增加,算法性能下降。由于每个内存块都占用8个字节的控制字段,大量小内存会导致内存的浪费。
查找过程中,发现两个邻居内存块是地址连续的,那么把这两个内存块合并成为一个内存块,称为内存整理。内存整理能够提升算法性能,但提升有限。其它优化方式,可以从查找算法进行优化,如二叉树查找等。

内存整理举例:
假设下图为多次内存分配和释放后的结构,第一块被占用,第二块,第三块,第四块为空闲内存块。
第二块大小为64字节,第三块大小为64字节,第四块内存大小为256字节。
假如现在申请分配内存大小为128字节,在分配查找过程中,发现第二块太小不满足,但第二块和第三块地址连续,于是把第二块和第三块合并为一块,并且发现合并后正好满足请求,返回给应用程序,如下面第二个图。

Threadx 内存管理-内存字节池_第6张图片

Threadx 内存管理-内存字节池_第7张图片

字节池内存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);
}

你可能感兴趣的:(threadx,RTOS)