ThreadX学习(4)——内存管理
学习参考
- 《Real-Time Embedded Multithreading: Using ThreadX and ARM》
(链接:https://pan.baidu.com/s/1GMScXSlHq13yS4AVxP_zPw 提取码:ysuw ) - 安富莱_STM32-V7开发板ThreadX内核教程(V0.7)——硬汉嵌入式论坛
内存分配
ThreadX中的内存分配有两种方法:内存字节池和内存块池。
用户可提前创建内存池,在之后可以随意分配池里的空间。
一般而言,字节块池更能满足用户的各种需求。
由于可能存在内存池空间不足,需要等待被占用空间释放的情况,所以每个内存池也有类似于互斥锁挂起列表的数据结构,即内存池挂起列表。
内存字节池 memory byte pool
顾名思义,内存字节池是可用于任何资源的字节的顺序集合。
内存字节池类似于一个标准的C语言堆。与C堆不同,内存字节池的数量没有限制。此外,线程可以挂起在池上,直到请求的内存可用为止。
内存字节池中的分配基于指定的字节数。ThreadX以优先匹配的方式从字节池中分配内存,即使用满足请求的第一个空闲内存块。
字节池大小
内存字节池中可分配的字节数略小于创建期间指定的字节数。这是因为对空闲内存区域的管理引入了一些开销。池中的每个空闲内存块需要相当于两个C指针的开销。
此外,当创建池时,ThreadX会自动将其划分为两个块,一个是大的空闲块,另一个是内存区域末端永久分配的小块。
该分配的末端块用于提高分配算法的性能。它消除了在合并期间不断检查池区域末端的需要。
当分配奇数个字节时,ThreadX填充块以确保下一个内存块的正确对齐。此外,随着池变得更加碎片化,开销也会增加。
碎片整理
来自这个块的多余内存被转换为一个新的块,并放回空闲内存列表中,这通常会导致碎片问题。
在随后的分配搜索(tx_byte_pool_search)中,如果第一个找到的空闲块大小不足,则会向后合并空闲块,若这一段空闲区域全部合并也无法满足大小,则寻找下一个空闲块区域,以获取足够大的空闲内存块。这个过程称为碎片整理。
也就是说,碎片整理过程随着搜索过程进行的。
API
1.tx_byte_pool_create
TX_BYTE_POOL my_pool;//先定义一个内存字节池
UINT tx_byte_pool_create( TX_BYTE_POOL *pool_ptr,
CHAR *name_ptr,
VOID *pool_start,
ULONG pool_size)
创建内存字节池:
- 第 1 个参数 pool_ptr 是内存字节池控制块指针。
- 第 2 个参数 name_ptr 是字节池名。
- 第 3 个参数 pool_start 是字节池的首地址。
- 第 4 个参数 pool_size 是字节池的长度。
- 返回值:
- TX_SUCCESS(0x00)成功创建字节池。
- TX_POOL_ERROR:(0x02)无效的内存池指针。要么指针是NULL,要么池已经创建。
- TX_PTR_ERROR: (0x03)无效的池起始地址。
- TX_SIZE_ERROR: (0x05)池大小无效。
- TX_CALLER_ERROR:(0x13)无效的服务调用者。
2.tx_byte_allocate
UINT tx_byte_allocate( TX_BYTE_POOL *pool_ptr,
VOID **memory_ptr,
ULONG memory_size,
ULONG wait_option)
分配内存字节池空间:
- 第 1 个参数 pool_ptr 是内存字节池控制块指针。
- 第 2 个参数 memory_ptr 是字节池分配空间首地址的指针。
- 第 3 个参数 memory_size是字节池分配空间大小。
- 第 4 个参数 wait_option是字节池分配等待选项:
- TX_NO_WAIT:不等待,直接返回结果。
- TX_WAIT_FOREVER:一直等待直到获取字节池空间。
- timeout value:设置等待时间(时钟脉冲)。
- 返回值:
- TX_SUCCESS(0x00)成功分配字节池。
- TX_DELETED: (0x01)线程挂起时内存池被删除。
- TX_NO_MEMORY: (0x10) 无法在指定的等待时间内分配内存。
- TX_WAIT_ABORTED: (0x1A)挂起被另一个线程、计时器或ISR中止。
- TX_POOL_ERROR:(0x02)无效的内存池指针。
- TX_PTR_ERROR:(0x03)指向目标指针的指针无效。
- TX_SIZE_ERROR: (0X05)请求的大小是0或大于池的总大小。
- TX_WAIT_ERROR: (0x04)在非线程调用时指定了TX_NO_WAIT以外的等待选项。
- TX_CALLER_ERROR:(0x13)无效的服务调用者。
3.tx_byte_pool_delete
UINT tx_byte_pool_delete(TX_BYTE_POOL *pool_ptr)
删除内存字节池:
- 第 1 个参数 pool_ptr 是内存字节池控制块指针。
- 返回值:
- TX_SUCCESS(0x00)成功删除字节池。
- TX_POOL_ERROR:(0x02)无效的内存池指针。
- TX_CALLER_ERROR:(0x13)无效的服务调用者。
字节池挂起的所有线程都将恢复,并接收TX_DELETED返回状态。
4.tx_byte_pool_info_get
UINT tx_byte_pool_info_get(TX_BYTE_POOL *pool_ptr,
CHAR **name,
ULONG *available_bytes,
ULONG *fragments,
TX_THREAD **first_suspended,
ULONG *suspended_count,
TX_BYTE_POOL **next_pool)
获取字节池信息:
- 第 1 个参数 pool_ptr 是内存字节池控制块指针。
- 第 2 个参数 name 是字节池名字符串,获取后存储的指针。
- 第 3 个参数 available_bytes 是可用字节数,获取后存储的指针。
- 第 4 个参数 fragments 是碎片数量,获取后存储的指针。
- 第 5 个参数 first_suspended 是等待该字节池的第一个线程TCB指针,获取后存储的指针。
- 第 6 个参数 suspended_count 是等待该字节池的线程数,获取后存储的指针。
- 第 7 个参数 next_pool 是字节池列表的下一个字节池控制块指针,获取后存储的指针。
- 返回值:
- TX_SUCCESS(0x00)成功获取字节池信息。
- TX_POOL_ERROR:(0x02)无效的内存池指针。
5.tx_byte_pool_prioritize
UINT tx_byte_pool_prioritize(TX_BYTE_POOL *pool_ptr)
字节池挂起列表的最高优先级线程置队头:
- 第 1 个参数 pool_ptr 是内存字节池控制块指针。
- 返回值:
- TX_SUCCESS(0x00)成功或挂起列表为空。
- TX_POOL_ERROR:(0x02)无效的内存池指针。
6.tx_byte_release
UINT tx_byte_release(VOID *memory_ptr)
字节池释放空间:
- 第 1 个参数 memory_ptr 是要释放的空间首地址。
- 返回值:
- TX_SUCCESS(0x00)成功释放。
- TX_PTR_ERROR:(0x03)无效的内存区域指针。
- TX_CALLER_ERROR:(0x13)无效的服务调用者。
tx_byte_release将先前分配的内存区域释放,挂起列表按顺序开始分配,直至内存池空间不足或挂起列表为空。
内存块池 Memory block pool
优点
通常,内存块池优于内存字节池,因为可以消除碎片问题,而且对池的分配和释放更快。
内存块池分配和释放时不必搜索块列表,它总是在可用列表的开头分配和释放内存块。
这提供了尽可能快的链表处理,并可能有助于将当前使用的内存块保持在缓存中。
缺点
缺乏灵活性是固定大小内存池的主要缺点。
池的块大小必须足够大,以处理其用户最坏情况下的内存需求。从同一个池中发出许多不同大小的内存请求可能会造成内存浪费。
一种可能的解决方案是创建几个不同的内存块池,其中包含不同大小的内存块。
内存块池大小
内存块池的大小除了每个内存块的大小和之外,还包括每个内存块的指针(一般包括所有者指针和下一个内存块指针)。
内存块数量 = 内存块池总大小 / (每个内存块大小 + 每块的指针大小)
比如一个1000B的内存块池,每个内存块50B,指针4B,那么:
内存块数量 = 1000 / (50 + 4)= 18.52 块
API
1.tx_block_pool_create
TX_BLOCK_POOL my_pool;//先定义一个内存块池
UINT tx_block_pool_create( TX_BLOCK_POOL *pool_ptr,
CHAR *name_ptr,
ULONG block_size,
VOID *pool_start,
ULONG pool_size)
创建内存字节池:
- 第 1 个参数 pool_ptr 是内存块池控制块指针。
- 第 2 个参数 name_ptr 是内存块池名。
- 第 3 个参数 block_size 是单个内存块的大小。
- 第 4 个参数 pool_start 是内存块池的首地址。
- 第 5 个参数 pool_size 是内存块池的总大小。
- 返回值:
- TX_SUCCESS(0x00)成功创建内存块池。
- TX_POOL_ERROR:(0x02)无效的内存块池指针。要么指针是NULL,要么池已经创建。
- TX_PTR_ERROR: (0x03)无效的池起始地址。
- TX_SIZE_ERROR: (0x05)池大小无效。
- TX_CALLER_ERROR:(0x13)无效的服务调用者。
2.tx_block_allocate
UINT tx_block_allocate(TX_BLOCK_POOL *pool_ptr,
VOID **block_ptr,
ULONG wait_option)
分配内存块:
- 第 1 个参数 pool_ptr 是内存块池控制块指针。
- 第 2 个参数 block_ptr 是分配得到的内存块首地址的指针,需要提前定义。
- 第 3 个参数 wait_option 是内存块池分配等待选项:
- TX_NO_WAIT:不等待,直接返回结果。
- TX_WAIT_FOREVER:一直等待直到获取内存块。
- timeout value:设置等待时间(时钟脉冲)。
- 返回值:
- TX_SUCCESS(0x00)成功分配。
- TX_DELETED: (0x01)线程挂起时内存块池被删除。
- TX_NO_MEMORY: (0x10) 无法在指定的等待时间内分配内存块。
- TX_WAIT_ABORTED: (0x1A)挂起被另一个线程、定时器或ISR中止。
- TX_POOL_ERROR:(0x02)无效的内存块池指针。
- TX_PTR_ERROR:(0x03)无效的分配指针。
- TX_WAIT_ERROR: (0x04)在非线程调用时指定了TX_NO_WAIT以外的等待选项。
3.tx_block_pool_delete
UINT tx_block_pool_delete(TX_BLOCK_POOL *pool_ptr)
删除内存块池:
- 第 1 个参数 pool_ptr 是内存块池控制块指针。
- 返回值:
- TX_SUCCESS(0x00)成功删除内存块池。
- TX_POOL_ERROR:(0x02)无效的内存块池指针。
- TX_CALLER_ERROR:(0x13)无效的服务调用者。
内存块池挂起的所有线程都将恢复,并接收TX_DELETED返回状态。
4.tx_block_pool_info_get
UINT tx_block_pool_info_get( TX_BLOCK_POOL *pool_ptr,
CHAR **name,
ULONG *available_blocks,
ULONG *total_blocks,
TX_THREAD **first_suspended,
ULONG *suspended_count,
TX_BLOCK_POOL **next_pool)
获取内存块池信息:
- 第 1 个参数 pool_ptr 是内存块池控制块指针。
- 第 2 个参数 name 是内存块池名字符串,获取后存储的指针。
- 第 3 个参数 available_blocks 是可用内存块数,获取后存储的指针。
- 第 4 个参数 total_blocks 是内存块总数,获取后存储的指针。
- 第 5 个参数 first_suspended 是等待该内存块池的第一个线程TCB指针,获取后存储的指针。
- 第 6 个参数 suspended_count 是等待该内存块池的线程数,获取后存储的指针。
- 第 7 个参数 next_pool 是内存块池列表的下一个内存块池控制块指针,获取后存储的指针。
- 返回值:
- TX_SUCCESS(0x00)成功获取内存块池信息。
- TX_POOL_ERROR:(0x02)无效的内存块池指针。
5.tx_block_pool_prioritize
UINT tx_block_pool_prioritize(TX_BLOCK_POOL *pool_ptr)
内存块池挂起列表的最高优先级线程置队头:
- 第 1 个参数 pool_ptr 是内存块池控制块指针。
- 返回值:
- TX_SUCCESS(0x00)成功或挂起列表为空。
- TX_POOL_ERROR:(0x02)无效的内存块池指针。
6.tx_block_release
UINT tx_block_release(VOID *memory_ptr)
内存块释放:
- 第 1 个参数 memory_ptr 是要释放内存块首地址。
- 返回值:
- TX_SUCCESS(0x00)成功释放。
- TX_PTR_ERROR(0x03)无效的内存块指针。
tx_block_release将先前分配的内存块释放,挂起列表按顺序开始分配,直至可用内存块不足或挂起列表为空。