文章目录
- 内存池控制块
- 内存池链表
- 内存池初始化
- 内存分配
- 内存释放
- 内存整理
- 字节池内存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.*/
ULONGtx_byte_pool_id;
/* Define the byte pool's name.*/
CHAR_PTRtx_byte_pool_name;
/* Define the number of available bytes in the pool.*/
ULONGtx_byte_pool_available;
/* Define the number of fragments in the pool.*/
ULONGtx_byte_pool_fragments;
/* Define the head pointer of byte pool.*/
CHAR_PTRtx_byte_pool_list;
/* Define the search pointer used for initial searching for memory
in a byte pool.*/
CHAR_PTRtx_byte_pool_search;
/* Save the start address of the byte pool's memory area.*/
CHAR_PTRtx_byte_pool_start;
/* Save the byte pool's size in bytes.*/
ULONGtx_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;
ULONGtx_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_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_ALLOC0xAAAAAAAAUL
#define TX_BYTE_BLOCK_FREE0xFFFFEEEEUL
如下图,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_AREATX_BYTE_POOL*tail_ptr;
/* Working byte pool pointer*/
CHAR_PTRblock_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 【threadx|Threadx 内存管理-内存字节池】把内存池从_tx_byte_pool_created_ptr链表删除,恢复挂起链表中的线程。
UINT_tx_byte_pool_delete(TX_BYTE_POOL *pool_ptr)
{TX_INTERRUPT_SAVE_AREATX_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);
}
推荐阅读
- ucos 学习:STM32F107 学习板 资料收集
- ThreadX|ThreadX(四)------TraceX使用
- ThreadX|ThreadX(一) ------初识
- stm32|ucosii在stm32上的移植详解3
- rtos|ucosii在stm32上的移植详解1
- ThreadX|ThreadX(二) ------移植到STM32
- ThreadX|ThreadX应用开发笔记之一(移植ThreadX到STM32平台)