TLSF内存分配器:从原理到工业级实现的完整解析
摘要 :TLSF(Two-Level Segregated Fit)是一种专为实时系统设计的动态内存分配算法,其核心优势在于O(1)时间复杂度的分配/释放操作和出色的碎片控制能力。本文将深入解析TLSF的核心原理,并结合一份完整的工业级C语言实现代码,从数据结构、索引算法、内存分配/释放流程、线程安全机制到内存池扩展与缩减,逐一剖析其实现细节。无论你是嵌入式工程师、实时系统开发者,还是对内存管理底层原理感兴趣的程序员,这篇文章都将为你提供系统性的参考。
一、为什么需要TLSF?
在传统的内存分配算法中(如首次适应First-Fit、最佳适应Best-Fit),分配和释放操作的时间复杂度通常为O(n),且随着系统运行时间的增长,内存碎片问题会愈发严重。对于实时系统 (RTOS)、嵌入式设备 和对延迟极度敏感的应用场景(如音视频处理、工业控制、游戏引擎),这种不确定性是不可接受的。
TLSF算法的出现正是为了解决这些痛点:
| 特性 | 传统分配器 | TLSF |
|---|---|---|
| 分配时间复杂度 | O(n) ~ O(log n) | O(1) |
| 释放时间复杂度 | O(n) ~ O(log n) | O(1) |
| 最坏情况执行时间 | 不可预测 | 确定且恒定 |
| 内存碎片 | 较高 | 较低(<15%平均碎片率) |
| 适用场景 | 通用系统 | 实时/嵌入式/高并发 |
TLSF已被广泛应用于LVGL图形库、RT-Thread操作系统、xenomai实时框架等知名项目中,是实时内存管理的事实标准之一。
二、TLSF核心原理:两级分离适配
TLSF的核心思想可以用一句话概括:通过两级位图索引,将内存块按大小分级管理,实现常数时间的查找与分配。
2.1 两级索引结构
TLSF将内存块大小划分为两个层级进行管理:
第一级索引(First Level, FL):按2的幂次方划分大区间。例如:
- FL=0: [16, 31] 字节
- FL=1: [32, 63] 字节
- FL=2: [64, 127] 字节
- ...以此类推
第二级索引(Second Level, SL):在每个FL大区间内,再细分为若干等宽的子区间。例如将每个FL区间细分为8个子区间:
- SL=0: [16, 17], [32, 35], [64, 71]...
- SL=1: [18, 19], [36, 39], [72, 79]...
这样,任意一个内存块大小都可以被唯一映射到一个 [FL][SL] 坐标上。
2.2 位图加速查找
为了在不遍历链表的情况下快速定位可用内存块,TLSF使用了两级位图:
- 一级位图(Index_bit) :一个
Base_Type类型的位图,每一位对应一个FL层级。如果该位为1,表示该FL层级下存在空闲块。 - 二级位图(Map_bit[]):一个位图数组,每个元素对应一个FL层级下的SL位图。如果某一位为1,表示对应的SL子区间存在空闲块。
通过位操作(如 __builtin_ffs、__builtin_clz 等硬件指令),可以在常数时间内找到第一个满足条件的空闲块,无需遍历链表。
2.3 空闲链表矩阵
最终,TLSF维护了一个二维空闲链表矩阵:
c
List_Node* Free_List_Head[FREE_LIST_SIZE][sizeof(TPED_BIT) << 3];
每个 [FL][SL] 位置对应一个双向链表,存储该大小类别下的所有空闲块。分配时直接从链表头部取出即可,释放时通过位图快速定位插入位置。
三、数据结构深度解析
让我们结合代码,深入理解TLSF的核心数据结构。
3.1 基础节点(Base_Node)
c
typedef struct Base_Node
{
struct
{
Base_Type Block_Size : (sizeof(Base_Type) << 3) - MEMORY_BLOCK - 1;
Base_Type Memory_flag : MEMORY_BLOCK;
Base_Type Used_flag : 1;
};
void* Pre_addr;
} Base_Node;
设计亮点:
- 位域压缩 :
Block_Size、Memory_flag、Used_flag三个字段被压缩到一个Base_Type中,最大限度地减少元数据开销。 Block_Size占用绝大部分位宽,支持大内存块管理。Memory_flag用于标识该块属于哪个内存池(支持多内存池管理)。Used_flag标记块是否被占用。Pre_addr指向物理相邻的前一个块,这是实现相邻块合并的关键。
3.2 链表节点(List_Node)
c
typedef struct List_Node
{
Base_Node Base;
struct List_Node* Prev;
struct List_Node* Next;
} List_Node;
当块处于空闲状态时,它会被插入到空闲链表中,此时 Prev 和 Next 指针用于维护双向链表。当块被占用时,这两个指针可以被用户数据覆盖(因为空闲时才需要链表指针)。
3.3 内存管理头(Memory_Head)
c
typedef struct Memory_Head
{
Base_Type Total_Size; // 管理的内存总大小
Base_Type Available_Size; // 当前可用的内存大小
Base_Type Map_unit_size; // 位图单元大小(控制二级索引粒度)
void* End_Addr_Node; // 尾部哨兵块地址
Memory_Condition Condition; // 内存管理器当前状态
volatile Base_Type Memory_index;// 内存块索引
volatile Base_Type Index_bit; // 一级位图索引
volatile TPED_BIT Map_bit[FREE_LIST_SIZE]; // 二级位图索引
List_Node* Free_List_Head[FREE_LIST_SIZE][sizeof(TPED_BIT) << 3];
void (*Lock)(void); // 锁函数指针
void (*Unlock)(void); // 解锁函数指针
} Memory_Head;
关键设计:
Map_unit_size决定了二级索引的粒度。例如,当Map_unit_size=3时,每个FL区间被细分为2^3=8个子区间。Condition枚举(IDLE/MALLOC_ING/FREE_ING/REALLOC_ING/CALLOC_ING)用于线程安全的状态检测。- 通过函数指针
Lock/Unlock实现可插拔的线程同步机制,用户可以根据平台提供自旋锁、互斥锁或关中断等实现。
3.4 全局管理器(TLSF_Head)
c
typedef struct TLSF_Head
{
Base_Type Current_Memory_Num;
Memory_Head* Memory_Head[MEMORY_BLOCK];
} TLSF_Head;
支持管理多个独立的内存池,每个 Memory_Head 对应一块连续的物理内存区域。这对于需要隔离不同子系统内存的场景非常有用。
四、核心算法详解
4.1 索引计算:O(1)的基石
TLSF的索引计算是整个算法的核心,决定了如何将任意大小映射到 [FL][SL] 坐标。
c
// 获取最高位1的位置(即⌊log₂(x)⌋)
static inline int Get_highest_bit_position(Base_Type x)
{
return x ? ((int)((sizeof(x) << 3) - 1) - __builtin_clz(x)) : -1;
}
// 将大小映射到精确的[FL][SL]索引
static inline Base_Type Change_Index(Base_Type size, int Position_bit, Base_Type alignment)
{
return (Position_bit << alignment) |
(((0x01UL << alignment) - 1) & (size >> (Position_bit - alignment)));
}
// 计算第一个不小于请求大小的块索引(向上取整)
static inline Base_Type Align_Index(Base_Type size, Base_Type alignment)
{
int Position_bit = Get_highest_bit_position(size);
return (Change_Index(size, Position_bit, alignment) +
(((0x01UL << (Position_bit - alignment)) - 1) & size ? 1 : 0));
}
算法解析:
-
Get_highest_bit_position:利用GCC内置函数__builtin_clz(Count Leading Zeros)快速计算最高位位置,等价于求⌊log₂(x)⌋。对于非GCC编译器,代码提供了二分查找的软件实现作为兜底方案。 -
Change_Index:将大小分解为FL和SL两部分:Position_bit对应FL层级(即最高位位置)。alignment控制SL的细分粒度。- 低位通过右移和掩码提取SL索引。
-
Align_Index:实现向上取整。如果请求大小恰好落在某个SL区间的边界上,则直接使用;否则需要映射到下一个更大的区间。
4.2 空闲块查找:位图驱动的O(1)搜索
c
static int Find_Block(Memory_Head* Head, Base_Type Index_len, Base_Type x)
{
TPED_BIT Temp_bit = 0;
Base_Type Bit_mask = 0x01UL << Head->Map_unit_size;
Base_Type Bit_position = Head->Map_unit_size;
// 步骤1:检查当前FL层级的SL位图是否满足需求
if (Head->Map_bit[x >> Bit_position] < (0x01UL << (x & (Bit_mask - 1))))
{
// 当前SL区间没有足够大的块,需要查找更大的FL层级
Base_Type Index_Bit = Head->Index_bit &
(~(((0x01UL << (x >> Bit_position)) << 1) - 1));
if (Index_Bit)
x = (__builtin_ffs(Index_Bit) - 1) << Bit_position;
else
return -1;
}
// 步骤2:在确定的FL层级中,找到第一个满足条件的SL区间
Temp_bit = Head->Map_bit[x >> Bit_position] &
(~((0x01UL << (x & (Bit_mask - 1))) - 1));
return (x & ~(Bit_mask - 1)) | (__builtin_ffs(Temp_bit) - 1);
}
查找流程:
- 检查当前FL层级的SL位图:如果当前FL层级下,从指定SL位置开始的位图全为0,说明没有足够大的空闲块。
- 向上搜索更大的FL层级 :通过
Index_bit位图快速找到下一个存在空闲块的FL层级。这里使用了位掩码技巧:~(((1 << (x >> Bit_position)) << 1) - 1)用于屏蔽掉当前FL及以下的所有位。 - 在目标FL层级中定位SL :通过
__builtin_ffs(Find First Set)找到第一个满足条件的SL索引。
整个过程仅涉及几次位运算和数组索引,时间复杂度严格为O(1)。
4.3 内存分配流程
c
extern void* TLSF_malloc(Memory_Head* Head, Base_Type size)
{
if (Head == NULL || size == 0 || size > Head->Available_Size) return NULL;
// 1. 大小对齐:加上元数据开销并对齐
size += ALIGN_SIZE(sizeof(Base_Node), ALIGN_BYTE);
size = (size < BLOCK_Min_SIZE) ? ALIGN_SIZE(BLOCK_Min_SIZE, ALIGN_BYTE)
: ALIGN_SIZE(size, ALIGN_BYTE);
// 2. 计算索引并查找空闲块
Base_Type Index = Align_Index(size, Head->Map_unit_size);
int Node_index = Find_Block(Head, FREE_LIST_SIZE, Index);
if (Node_index == -1) return NULL;
// 3. 从空闲链表中取出块
List_Node* Node = Take_Out_Node(Head, Node_index);
// 4. 分割块(如果剩余部分足够大)
return (void*)((BYTE*)Partition_Node(Head, Node, size) + ALIGN_SIZE(sizeof(Base_Node), ALIGN_BYTE));
}
分配流程:
- 大小调整 :用户请求的大小需要加上
Base_Node的元数据开销,并按ALIGN_BYTE对齐。同时确保不小于BLOCK_Min_SIZE。 - 索引计算 :通过
Align_Index计算目标[FL][SL]索引。 - O(1)查找 :
Find_Block在位图中快速定位可用块。 - 取出并分割 :
Take_Out_Node从链表头部取出块;Partition_Node检查是否需要分割(如果剩余部分 ≥BLOCK_Min_SIZE,则分割出一个新的空闲块)。 - 返回用户指针 :返回的地址跳过了
Base_Node元数据区域,指向实际可用的用户数据区。
4.4 内存释放与合并
c
extern void TLSF_free(Memory_Head* Head, void* addr)
{
if (Head == NULL || addr == NULL) return;
// 回退到块头,执行合并
Merge_Node(Head, (BYTE*)addr - ALIGN_SIZE(sizeof(Base_Node), ALIGN_BYTE));
}
释放操作的核心是 Merge_Node:
c
static void Merge_Node(Memory_Head* Head, void* addr)
{
List_Node* Node = (List_Node*)addr;
List_Node* Pre_Node = (List_Node*)Node->Base.Pre_addr;
List_Node* Next_Node = (List_Node*)((BYTE*)Node + Node->Base.Block_Size);
// 向前合并:检查前一个物理相邻块是否空闲
if (Pre_Node && !Pre_Node->Base.Used_flag)
{
Delete_Node(Head, Pre_Node); // 从空闲链表中移除前块
Pre_Node->Base.Block_Size += Node->Base.Block_Size; // 合并大小
Next_Node->Base.Pre_addr = Pre_Node; // 更新后块的Pre_addr
Node = Pre_Node; // 当前节点变为前块
}
// 向后合并:检查后一个物理相邻块是否空闲
if (!Next_Node->Base.Used_flag)
{
Delete_Node(Head, Next_Node); // 从空闲链表中移除后块
Node->Base.Block_Size += Next_Node->Base.Block_Size; // 合并大小
Next_Node = (List_Node*)((BYTE*)Node + Node->Base.Block_Size);
Next_Node->Base.Pre_addr = Node; // 更新新后块的Pre_addr
}
// 将合并后的块加入空闲链表
Add_Node(Head, Node);
}
合并策略:
- 即时合并(Immediate Coalescing):释放时立即检查前后相邻块,如果相邻块空闲则立即合并。这能有效控制外部碎片。
- 通过
Pre_addr指针实现O(1)的前向查找(无需遍历)。 - 后向查找通过当前块地址 + 块大小直接计算。
4.5 内存重分配(realloc)优化
TLSF的 realloc 实现非常精巧,尽可能在原地扩展,避免数据拷贝:
c
extern void* TLSF_realloc(Memory_Head* Head, void* addr, Base_Type size)
{
// ... 参数校验 ...
List_Node* Node = (List_Node*)((BYTE*)addr - ALIGN_SIZE(sizeof(Base_Node), ALIGN_BYTE));
Base_Type Old_size = Node->Base.Block_Size;
List_Node* Next_Node = (List_Node*)((BYTE*)Node + Old_size);
size = ALIGN_SIZE(size, ALIGN_BYTE) + ALIGN_SIZE(sizeof(Base_Node), ALIGN_BYTE);
size = (size < BLOCK_Min_SIZE) ? ALIGN_SIZE(BLOCK_Min_SIZE, ALIGN_BYTE) : ALIGN_SIZE(size, ALIGN_BYTE);
if (Old_size >= size)
{
// 情况1:当前块足够大,直接分割
if (Old_size >= (size + BLOCK_Min_SIZE))
{
// 分割出剩余空闲块,并尝试与后向空闲块合并
// ...
}
}
else
{
// 情况2:当前块不够大
if (Next_Node->Base.Used_flag == 0 && (Next_Node->Base.Block_Size + Old_size) >= size)
{
// 情况2a:后向相邻块空闲且合并后足够大
// 合并当前块 + 后向块,按需分割
// ...
}
else
{
// 情况2b:无法原地扩展,分配新块并拷贝数据
void* New_Node = TLSF_malloc(Head, size - ALIGN_SIZE(sizeof(Base_Node), ALIGN_BYTE));
if (New_Node == NULL) return NULL;
Memcpy_s(New_Node, addr, Old_size - ALIGN_SIZE(sizeof(Base_Node), ALIGN_BYTE));
TLSF_free(Head, addr);
addr = New_Node;
}
}
return addr;
}
优化策略:
- 原地收缩:如果当前块比请求大,直接分割,无需任何数据移动。
- 原地扩展(后向合并):如果后向相邻块空闲,且合并后足够大,则直接合并并分割,同样无需数据移动。
- 异地重分配:仅当前两种策略都失败时,才分配新块并拷贝数据。
这种设计在大多数 realloc 场景下(尤其是扩展少量内存时)都能避免昂贵的数据拷贝。
五、线程安全机制
这份实现提供了三层线程安全策略,从简单到复杂:
5.1 基础线程安全(Safe系列)
c
extern void* TLSF_malloc_Safe(Memory_Head* Head, Base_Type size)
{
if (Head == NULL || size == 0) return NULL;
if (Head->Lock) Head->Lock(); // 获取锁
Head->Condition = MALLOC_ING; // 设置状态
void* ptr = TLSF_malloc(Head, size);
if (Head->Unlock) Head->Unlock(); // 释放锁
Head->Condition = IDLE;
return ptr;
}
通过函数指针注入锁机制,用户可以根据平台特性选择:
- RTOS环境:关中断 / 任务调度锁
- 多核环境:自旋锁(Spinlock)
- 通用环境:互斥锁(Mutex)
5.2 全局线程安全(Thread_Safe系列)
c
extern void* Thread_Safe_TLSF_malloc(Base_Type size)
{
Base_Type i = 0;
// 轮询查找空闲的内存池
while (TLSF_Manager.Memory_Head[i % MEMORY_BLOCK]->Condition != IDLE) i++;
return TLSF_malloc_Safe(TLSF_Manager.Memory_Head[i], size);
}
支持多内存池的负载均衡:当某个内存池正被占用时,自动轮询到下一个空闲的内存池。这种设计适用于高并发场景下的内存分配压力分散。
5.3 状态检测机制
c
typedef enum Memory_Condition
{
IDLE, MALLOC_ING, FREE_ING, REALLOC_ING, CALLOC_ING
} Memory_Condition;
通过 Condition 字段,实现了无锁的忙等待检测。在 Thread_Safe_TLSF_malloc 中,通过轮询 Condition != IDLE 来判断内存池是否被占用。虽然这是一种简单的忙等待,但在实时系统中,由于临界区极短,这种开销是可以接受的。
六、内存池动态扩展与缩减
这份实现还提供了内存池的动态管理能力,这在长期运行的系统中非常有价值。
6.1 扩展内存空间(Extend_Memory_Space)
c
extern void Extend_Memory_Space(Memory_Head* Head, Base_Type size)
{
// ... 参数校验 ...
List_Node* End_addr = Head->End_Addr_Node;
List_Node* End_Pre_addr = End_addr->Base.Pre_addr;
List_Node* Extend_Node = NULL;
if (!End_Pre_addr->Base.Used_flag)
{
// 尾块前一个是空闲块:直接扩展该空闲块
Extend_Node = End_Pre_addr;
Delete_Node(Head, End_Pre_addr);
Extend_Node->Base.Block_Size += End_addr->Base.Block_Size + size;
}
else
{
// 尾块前一个是占用块:扩展尾部哨兵块本身
Extend_Node = End_addr;
Extend_Node->Base.Block_Size += size;
}
// 重新设置尾部哨兵块
End_addr = (List_Node*)((BYTE*)Extend_Node + (Extend_Node->Base.Block_Size - BLOCK_Min_SIZE));
End_addr->Base.Used_flag = 1;
End_addr->Base.Block_Size = BLOCK_Min_SIZE;
End_addr->Base.Pre_addr = Extend_Node;
Extend_Node->Base.Block_Size -= BLOCK_Min_SIZE;
Head->Total_Size += size;
Add_Node(Head, Extend_Node); // 将扩展后的空闲块加入链表
Head->End_Addr_Node = End_addr;
}
设计要点:
- 内存扩展时,优先与尾部空闲块合并,避免产生新的碎片。
- 始终保留一个尾部哨兵块(
BLOCK_Min_SIZE),用于标记内存边界,防止越界合并。
6.2 缩减内存空间(Shrinkage_Memory)
c
extern Base_Type Shrinkage_Memory(Memory_Head* Head, Base_Type Block_num, Base_Type Block_size)
{
// ... 参数校验 ...
List_Node* End_addr = Head->End_Addr_Node;
List_Node* End_Pre_addr = End_addr->Base.Pre_addr;
if (End_Pre_addr->Base.Used_flag) return 0; // 尾块被占用,无法缩减
Base_Type Shrink_num = End_Pre_addr->Base.Block_Size / Block_size;
Shrink_num = Shrink_num < Block_num ? Shrink_num : Block_num;
Base_Type Remain = End_Pre_addr->Base.Block_Size - (Shrink_num * Block_size);
// 余量保护:确保剩余块不小于最小块大小
if (Remain && Remain < BLOCK_Min_SIZE)
{
if (Shrink_num * Block_size < (BLOCK_Min_SIZE - Remain)) return 0;
Shrink_num = (End_Pre_addr->Base.Block_Size - BLOCK_Min_SIZE) / Block_size;
Remain = End_Pre_addr->Base.Block_Size - (Shrink_num * Block_size);
}
if (Shrink_num == 0) return 0;
Base_Type Reduct_byte = Shrink_num * Block_size;
Delete_Node(Head, End_Pre_addr);
End_Pre_addr->Base.Block_Size -= Reduct_byte;
// 重新设置尾部哨兵块位置
End_addr = (List_Node*)((BYTE*)End_Pre_addr + End_Pre_addr->Base.Block_Size);
End_addr->Base.Used_flag = 1;
End_addr->Base.Block_Size = BLOCK_Min_SIZE;
if (Remain) End_addr->Base.Pre_addr = End_Pre_addr, Add_Node(Head, End_Pre_addr);
Head->End_Addr_Node = End_addr;
Head->Total_Size -= Reduct_byte;
return Reduct_byte;
}
缩减策略:
- 只能从尾部缩减,且要求尾部块为空闲状态。
- 支持按固定块大小(
Block_size)批量缩减,适用于需要归还物理内存页的场景。 - 余量保护机制 :如果缩减后剩余空间小于
BLOCK_Min_SIZE,则调整缩减数量,确保剩余块仍可被正常管理。
七、代码亮点与工程实践
7.1 编译器优化适配
c
#if defined(__GNUC__) || defined(__clang__)
static inline int Get_highest_bit_position(Base_Type x)
{
return x ? ((int)((sizeof(x) << 3) - 1) - __builtin_clz(x)) : -1;
}
#else
static int Get_highest_bit_position(Base_Type x)
{
// 软件二分查找实现
int high_bit = sizeof(x) << 3, low_bit = 0;
// ... 二分查找代码 ...
}
#endif
对于GCC/Clang编译器,直接使用硬件指令 __builtin_clz 和 __builtin_ffs 实现O(1)的位操作;对于其他编译器,提供纯软件实现的二分查找作为兜底方案。这种设计兼顾了性能和可移植性。
7.2 跨平台类型抽象
c
#if CONFIG_SYSTEM_BIT == 16
typedef UINT_16 Base_Type;
#elif CONFIG_SYSTEM_BIT == 32
typedef UINT_32 Base_Type;
#elif CONFIG_SYSTEM_BIT == 64
typedef UINT_64 Base_Type;
#endif
通过 CONFIG_SYSTEM_BIT 宏,可以灵活适配16位、32位和64位系统,这在嵌入式开发中尤为重要。
7.3 内存对齐处理
c
#define ALIGN_SIZE(X, Align_Byte) ((X + Align_Byte - 1) & ~(Align_Byte - 1))
使用位运算实现高效的对齐计算,避免了除法和取模运算。所有块大小、地址都按 ALIGN_BYTE(默认4字节)对齐,确保在ARM等平台上不会出现非对齐访问异常。
7.4 安全的内存操作
c
extern void* Memcpy_s(void* destinction, void* orignal, Base_Type Length)
{
// 检测内存重叠,自动选择拷贝方向
if ((dest > orign) && ((orign + Length) > dest))
{
// 反向拷贝(从高地址到低地址)
}
else
{
// 正向拷贝(从低地址到高地址)
}
}
自定义的 Memcpy_s 函数检测内存重叠情况,自动选择安全的拷贝方向,避免了标准 memcpy 在重叠场景下的未定义行为。
八、代码实现
1、源码实现
TLSF.h文件实现
c
#ifndef TLSF_H
#define TLSF_H
#define ALIGN_BYTE 4
#define BIT_SIZE_CONFIG 8
#define MEMORY_BLOCK 1
#define CONFIG_SYSTEM_BIT 32
typedef unsigned char BYTE;
typedef unsigned char UINT_8;
typedef unsigned short UINT_16;
typedef unsigned int UINT_32;
typedef unsigned long long UINT_64;
#define BLOCK_Min_SIZE (sizeof(List_Node))
#if CONFIG_SYSTEM_BIT == 16
typedef UINT_16 Base_Type;
#elif CONFIG_SYSTEM_BIT == 32
typedef UINT_32 Base_Type;
#elif CONFIG_SYSTEM_BIT == 64
typedef UINT_64 Base_Type;
#endif
#define FREE_LIST_SIZE (sizeof(Base_Type)<<3)
#ifndef NULL
#define NULL ((void*)0)
#endif
#if BIT_SIZE_CONFIG==8
typedef UINT_8 TPED_BIT;
#elif BIT_SIZE_CONFIG==16
typedef UINT_16 TPED_BIT;
#endif
typedef enum Memory_Condition
{
IDLE,MALLOC_ING,FREE_ING,REALLOC_ING,CALLOC_ING
}Memory_Condition;
typedef struct Base_Node
{
struct
{
Base_Type Block_Size : (sizeof(Base_Type) << 3) - MEMORY_BLOCK - 1;
Base_Type Memory_flag : MEMORY_BLOCK;
Base_Type Used_flag : 1;
};
void* Pre_addr;
}Base_Node;
typedef struct List_Node
{
Base_Node Base;
struct List_Node* Prev;
struct List_Node* Next;
}List_Node;
typedef struct Memory_Head
{
Base_Type Total_Size;//管理的内存总大小
Base_Type Available_Size;//当前可用的内存大小
Base_Type Map_unit_size;//位图单元大小,单位是字节
void* End_Addr_Node;//尾部哨兵块地址
Memory_Condition Condition;//内存管理器当前状态
volatile Base_Type Memory_index;//内存块索引,支持多个内存块的管理
volatile Base_Type Index_bit;//一级位图索引
volatile TPED_BIT Map_bit[FREE_LIST_SIZE];//二级位图索引
List_Node* Free_List_Head[FREE_LIST_SIZE][sizeof(TPED_BIT) << 3];//TLSF数组,其中TPED_BIT类型的字节长度控制着二级索引的长度
void (*Lock)(void);//锁函数指针
void (*Unlock)(void);//解锁函数指针s
}Memory_Head;
typedef struct TLSF_Head
{
Base_Type Current_Memory_Num;//当前内存块数量
Memory_Head* Memory_Head[MEMORY_BLOCK];//内存管理器头指针数组,支持多个内存块的管理
}TLSF_Head;
//函数声明实现
static int Get_highest_bit_position(Base_Type x);
static int Find_Block(Memory_Head* Head, Base_Type Index_len, Base_Type x);
static Base_Type Change_Index(Base_Type size, int Position_bit, Base_Type alignment);
static Base_Type Align_Index(Base_Type size, Base_Type alignment);
static void Add_Node(Memory_Head* Head, List_Node* Node);
static List_Node* Take_Out_Node(Memory_Head* Head, Base_Type Index);
static void Delete_Node(Memory_Head* Head, List_Node* Node);
static void* Partition_Node(Memory_Head* Head, void* addr, Base_Type size);
static void Merge_Node(Memory_Head* Head, void* addr);
extern Base_Type Init_TLSF(Memory_Head* Head, void* Memory_addr, Base_Type Memory_size, void (*Lock)(void), void (*Unlock)(void));
extern void* TLSF_malloc(Memory_Head* Head, Base_Type size);
extern void TLSF_free(Memory_Head* Head, void* addr);
extern void* TLSF_realloc(Memory_Head* Head, void* addr, Base_Type size);
extern void* TLSF_calloc(Memory_Head* Head, Base_Type num, Base_Type size);
extern void* TLSF_malloc_Safe(Memory_Head* Head, Base_Type size);
extern void TLSF_free_Safe(Memory_Head* Head, void* addr);
extern void* TLSF_realloc_Safe(Memory_Head* Head, void* addr, Base_Type size);
extern void* TLSF_calloc_Safe(Memory_Head* Head, Base_Type num, Base_Type size);
extern void* Thread_Safe_TLSF_malloc(Base_Type size);
extern void Thread_Safe_TLSF_free(void* addr);
extern void* Thread_Safe_TLSF_realloc(void* addr, Base_Type size);
extern void* Thread_Safe_TLSF_calloc(Base_Type num, Base_Type size);
extern void Extend_Memory_Space(Memory_Head* Head, Base_Type size);
extern Base_Type Shrinkage_Memory(Memory_Head* Head, Base_Type Block_num, Base_Type Block_size);
extern Thread_Extend_Memory_Space_Safe(Memory_Head* Head, Base_Type size);
extern Thread_Shrinkage_Memory_Safe(Memory_Head* Head, Base_Type Block_num, Base_Type Block_size);
extern void Memory_Reset(void* addr, Base_Type Length);
extern void* Memcpy_s(void* destinction, void* orignal, Base_Type Length);
extern Base_Type Get_Head_Total(Memory_Head* Head);
extern Base_Type Get_Current_Available(Memory_Head* Head);
extern Base_Type Get_Node_Size(Memory_Head* Head, void* addr);
#endif
TLSF.c文件实现
c
#include "TLSF.h"
TLSF_Head TLSF_Manager = { 0 };
#define ALIGN_SIZE(X,Align_Byte) ((X + Align_Byte - 1) & ~(Align_Byte - 1))
#if defined(__GNUC__) || defined(__clang__)
static inline int Get_highest_bit_position(Base_Type x)
{
return x ? ((int)((sizeof(x) << 3) - 1) - __builtin_clz(x)) : -1;
}
static int Find_Block(Memory_Head* Head, Base_Type Index_len, Base_Type x)
{
TPED_BIT Temp_bit = 0;
Base_Type Bit_mask = 0x01UL << Head->Map_unit_size;
Base_Type Bit_position = Head->Map_unit_size;
if (Head->Map_bit[x >> Bit_position] < (0x01UL << (x & (Bit_mask - 1))))
{
Base_Type Index_Bit = Head->Index_bit & (~(((0x01UL << (x >> Bit_position)) << 1) - 1));
if (Index_Bit) x = (__builtin_ffs(Index_Bit) - 1) << Bit_position; else return -1;
}
Temp_bit = Head->Map_bit[x >> Bit_position] & (~((0x01UL << (x & (Bit_mask - 1))) - 1));
return (x & ~(Bit_mask - 1)) | (__builtin_ffs(Temp_bit) - 1);
}
#else
static int Get_highest_bit_position(Base_Type x)
{//此函数是软件实现的二分查找算法,用于找到一个无符号整数x中最高位1所在的位置。如果硬件支持该功能最好,可以直接使用内置函数来实现这个功能,例如GCC中的__builtin_clz函数。
int high_bit = sizeof(x) << 3, low_bit = 0;
int position = (high_bit + low_bit) >> 1;
if (x == 0) return -1;
while ((x >> position) ^ 0x01UL)
{
if (x >> position) low_bit = position; else high_bit = position;
position = (high_bit + low_bit) >> 1;
}
return position;
}
static int Find_Block(Memory_Head* Head, Base_Type Index_len, Base_Type x)
{//此函数用于在内存管理器的位图中查找第一个不小于x的块。如果能用硬件指令实现最好,可以直接使用内置函数来实现这个功能,例如GCC中的__builtin_ffs函数。
TPED_BIT Temp_bit = 0;
Base_Type Bit_mask = 0x01UL << Head->Map_unit_size;
Base_Type Bit_position = Head->Map_unit_size;
if (Head->Map_bit[x >> Bit_position] < (0x01UL << (x & (Bit_mask - 1))))
{
x += Bit_mask - (x & (Bit_mask - 1));
while ((x >> Bit_position) < Index_len && !Head->Map_bit[x >> Bit_position]) x += Bit_mask;
}
if ((x >> Bit_position) >= Index_len) return -1;
Temp_bit = Head->Map_bit[x >> Bit_position];
for (Base_Type i = (x & (Bit_mask - 1)); i < Bit_mask; i++, x++)
{
if (Temp_bit & (0x01UL << i)) return x;
}
return -1;
}
#endif
//将数值分解成不小于16的块,块的大小为2的幂次方,并返回块在数组中的位置
static inline Base_Type Change_Index(Base_Type size, int Position_bit, Base_Type alignment)
{
return (Position_bit << alignment) | (((0x01UL << alignment) - 1) & (size >> (Position_bit - alignment)));
}
//将数值分解成不小于16的块,块的大小为2的幂次方,并返回第一个不小于x的块在数组中的位置
static inline Base_Type Align_Index(Base_Type size, Base_Type alignment)
{
int Position_bit = Get_highest_bit_position(size);
return (Change_Index(size, Position_bit, alignment) + (((0x01UL << (Position_bit - alignment)) - 1) & size ? 1 : 0));
}
// Add_Node函数用于将一个内存块节点添加到内存管理器的自由列表中。它首先计算出块大小对应的自由列表索引,然后将节点插入到该索引的链表头部,并更新相应的位图以标记该索引有可用块。最后,它将节点的使用标志设置为0,表示该块是空闲的。
static void Add_Node(Memory_Head* Head, List_Node* Node)
{
if (Node == NULL) return;
Base_Type Index = Change_Index(Node->Base.Block_Size, Get_highest_bit_position(Node->Base.Block_Size), Head->Map_unit_size);
List_Node** Current_Head = &Head->Free_List_Head[Index >> (Head->Map_unit_size)][Index & ((0x01UL << (Head->Map_unit_size)) - 1)];
Node->Base.Used_flag = 0;
Node->Prev = NULL;
Node->Next = *Current_Head;
if (*Current_Head) (*Current_Head)->Prev = Node;
Head->Index_bit |= (0x01UL << (Index >> (Head->Map_unit_size)));
Head->Map_bit[Index >> (Head->Map_unit_size)] |= (0x01UL << (Index & ((0x01UL << (Head->Map_unit_size)) - 1)));
*Current_Head = Node;
Node->Base.Memory_flag = Head->Memory_index;
Head->Available_Size += Node->Base.Block_Size;
}
// Take_Out_Node函数用于从内存管理器的自由列表中取出一个满足条件的内存块节点。它首先计算出块大小对应的自由列表索引,然后从该索引的链表头部取出一个节点,并更新相应的位图以标记该索引是否还有可用块。最后,它将节点的使用标志设置为1,表示该块已被占用,并返回该节点。
static List_Node* Take_Out_Node(Memory_Head* Head, Base_Type Index)
{
if (Head == NULL) return NULL;
List_Node** Current_Head = &Head->Free_List_Head[Index >> (Head->Map_unit_size)][Index & ((0x01UL << (Head->Map_unit_size)) - 1)];
List_Node* Node = *Current_Head;
*Current_Head = Node->Next;
if (*Current_Head) (*Current_Head)->Prev = NULL; else Head->Map_bit[Index >> (Head->Map_unit_size)] &= ~(0x01UL << (Index & ((0x01UL << (Head->Map_unit_size)) - 1)));
if (!Head->Map_bit[Index >> (Head->Map_unit_size)]) Head->Index_bit &= ~(0x01UL << (Index >> (Head->Map_unit_size)));
Node->Base.Used_flag = 1;
Head->Available_Size -= Node->Base.Block_Size;
return Node;
}
// Delete_Node函数用于将一个内存块节点从内存管理器的自由列表中删除。它首先获取节点的前一个节点和下一个节点,然后根据节点在链表中的位置更新前一个节点和下一个节点的指针。最后,它将节点的使用标志设置为1,表示该块已被占用,并更新相应的位图以标记该索引是否还有可用块。
static void Delete_Node(Memory_Head* Head, List_Node* Node)
{
if (Head == NULL || Node == NULL) return;
Base_Type Index = Change_Index(Node->Base.Block_Size, Get_highest_bit_position(Node->Base.Block_Size), Head->Map_unit_size);
List_Node* Prev_Node = Node->Prev, * Next_Node = Node->Next;
List_Node** Current_Head = &Head->Free_List_Head[Index >> (Head->Map_unit_size)][Index & ((0x01UL << (Head->Map_unit_size)) - 1)];
if (*Current_Head == Node) *Current_Head = Node->Next;
else Prev_Node->Next = Node->Next;
if (Next_Node) Next_Node->Prev = Node->Prev;
if (*Current_Head == NULL) Head->Map_bit[Index >> (Head->Map_unit_size)] &= ~(0x01UL << (Index & ((0x01UL << (Head->Map_unit_size)) - 1)));
if (!Head->Map_bit[Index >> (Head->Map_unit_size)]) Head->Index_bit &= ~(0x01UL << (Index >> (Head->Map_unit_size)));
Node->Base.Used_flag = 1;
Head->Available_Size -= Node->Base.Block_Size;
}
// Partition_Node函数用于将一个内存块节点分割成一个满足请求大小的块和一个剩余的空闲块。它首先检查节点的块大小是否足够大以容纳请求的大小加上最小块大小,如果是,则创建一个新的节点来表示剩余的空闲块,并将其添加到自由列表中。最后,它将原始节点的使用标志设置为1,表示该块已被占用,并返回该节点。
static void* Partition_Node(Memory_Head* Head, void* addr, Base_Type size)
{
List_Node* Node = (List_Node*)addr;
if (Node->Base.Block_Size >= (size + BLOCK_Min_SIZE))
{
List_Node* Divide_Node = (List_Node*)((BYTE*)addr + size);
List_Node* Next_Node = (List_Node*)((BYTE*)addr + Node->Base.Block_Size);
Divide_Node->Base.Block_Size = Node->Base.Block_Size - size, Divide_Node->Base.Pre_addr = addr;
Next_Node->Base.Pre_addr = Divide_Node;
Node->Base.Block_Size = size;
Add_Node(Head, Divide_Node);
}
return Node;
}
// Merge_Node函数用于将一个内存块节点与其相邻的空闲块合并成一个更大的块。它首先获取节点的前一个节点和下一个节点,然后检查它们是否为空闲块。如果前一个节点是空闲块,则将其从自由列表中删除,并将其块大小加到当前节点的块大小上,同时更新下一个节点的前一个地址指针。如果下一个节点是空闲块,则将其从自由列表中删除,并将其块大小加到当前节点的块大小上,同时更新下一个节点的前一个地址指针。最后,它将合并后的块添加回自由列表中。
static void Merge_Node(Memory_Head* Head, void* addr)
{
List_Node* Node = (List_Node*)addr;
List_Node* Pre_Node = (List_Node*)Node->Base.Pre_addr;
List_Node* Next_Node = (List_Node*)((BYTE*)Node + Node->Base.Block_Size);
if (Pre_Node && !Pre_Node->Base.Used_flag)
{
Delete_Node(Head, Pre_Node);
Pre_Node->Base.Block_Size += Node->Base.Block_Size;
Next_Node->Base.Pre_addr = Pre_Node;
Node = Pre_Node;
}
if (!Next_Node->Base.Used_flag)
{
Delete_Node(Head, Next_Node);
Node->Base.Block_Size += Next_Node->Base.Block_Size;
Next_Node = (List_Node*)((BYTE*)Node + Node->Base.Block_Size);
Next_Node->Base.Pre_addr = Node;
}
Add_Node(Head, Node);
}
// Init_TLSF函数用于初始化内存管理器。它首先检查输入的内存管理器头指针、内存地址和内存大小是否有效,如果无效则直接返回。然后,它将内存大小对齐到最近的对齐大小的倍数,并设置内存管理器头中的总大小、可用大小和位图单位大小等信息。接下来,它将整个内存区域划分为一个大的空闲块,并将其添加到自由列表中。最后,它在内存区域的末尾设置一个占用块,以标记内存区域的结束。
extern Base_Type Init_TLSF(Memory_Head* Head, void* Memory_addr, Base_Type Memory_size, void (*Lock)(void), void (*Unlock)(void))
{
if (Head == NULL || Memory_addr == NULL || ALIGN_BYTE == 0 || (ALIGN_BYTE & (ALIGN_BYTE - 1))) return 0;
if (Memory_size < BLOCK_Min_SIZE || BLOCK_Min_SIZE < sizeof(List_Node) || (BLOCK_Min_SIZE & (ALIGN_BYTE - 1))) return 0;
Memory_size &= ~(ALIGN_BYTE - 1);
Head->Total_Size = Memory_size;
Head->Available_Size = 0;
Head->Map_unit_size = Get_highest_bit_position(sizeof(Head->Map_bit[0]) << 3);
if (Get_highest_bit_position(BLOCK_Min_SIZE) < Head->Map_unit_size) return 0;
//初始化位图和自由列表
Head->Index_bit = 0;
for (Base_Type i = 0; i < FREE_LIST_SIZE; i++)
{
for (Base_Type j = 0; j < (sizeof(Head->Map_bit[0]) << 3); j++)
{
Head->Free_List_Head[i][j] = NULL;
}
Head->Map_bit[i] = 0;
}
//将整个内存区域划分为一个大的空闲块,并将其添加到自由列表中
for (int i = 0, j = 0; i < MEMORY_BLOCK; i++)
{
j = (i + Head->Memory_index) % MEMORY_BLOCK;
if (TLSF_Manager.Memory_Head[j] == NULL)
{
Head->Memory_index = j;
TLSF_Manager.Memory_Head[j] = Head;
TLSF_Manager.Current_Memory_Num++;
break;
}
}
List_Node* Node = (List_Node*)Memory_addr;
Node->Base.Pre_addr = NULL, Node->Base.Used_flag = 1, Node->Base.Block_Size = Memory_size - BLOCK_Min_SIZE, Node->Base.Memory_flag = Head->Memory_index;
Add_Node(Head, Node);
Node = (List_Node*)((BYTE*)Memory_addr + (Memory_size - BLOCK_Min_SIZE));
Node->Base.Pre_addr = Memory_addr, Node->Base.Used_flag = 1,Node->Base.Memory_flag = Head->Memory_index, Node->Base.Block_Size = BLOCK_Min_SIZE;
Head->End_Addr_Node = Node;
Head->Lock = Lock;
Head->Unlock = Unlock;
Head->Condition = IDLE;
return 1;
}
// TLSF_malloc函数用于在内存管理器中分配一块指定大小的内存。它首先检查输入的大小是否为零或大于可用大小,如果是,则返回NULL,表示无法进行分配。否则,它将请求的大小对齐到最近的对齐大小的倍数,并加上基本节点结构的大小,以考虑每个分配块将存储的元数据。然后,它确保总大小至少等于BLOCK_Min_SIZE定义的最小块大小。
extern void* TLSF_malloc(Memory_Head* Head, Base_Type size)
{
if (Head == NULL || size == 0 || size > Head->Available_Size) return NULL;
size += ALIGN_SIZE(sizeof(Base_Node), ALIGN_BYTE);
size = (size < BLOCK_Min_SIZE) ? ALIGN_SIZE(BLOCK_Min_SIZE, ALIGN_BYTE) : ALIGN_SIZE(size, ALIGN_BYTE);
Base_Type Index = Align_Index(size, Head->Map_unit_size);
int Node_index = Find_Block(Head, FREE_LIST_SIZE, Index);
if (Node_index == -1) return NULL;
List_Node* Node = Take_Out_Node(Head, Node_index);
return (void*)((BYTE*)Partition_Node(Head, Node, size) + ALIGN_SIZE(sizeof(Base_Node), ALIGN_BYTE));
}
// TLSF_free函数用于释放之前分配的内存块。它首先检查输入的内存管理器头指针和要释放的地址是否为NULL,如果是,则直接返回。否则,它调用Merge_Node函数将要释放的内存块与相邻的空闲块合并,并将合并后的块添加回自由列表中。
extern void TLSF_free(Memory_Head* Head, void* addr)
{
if (Head == NULL || addr == NULL) return;
Merge_Node(Head, (BYTE*)addr - ALIGN_SIZE(sizeof(Base_Node), ALIGN_BYTE));
}
// TLSF_realloc函数用于重新分配之前分配的内存块。它首先检查输入的内存管理器头指针和要重新分配的地址是否为NULL,如果是,则直接调用TLSF_malloc函数进行分配。然后,它检查请求的大小是否为零,如果是,则调用TLSF_free函数释放内存并返回NULL。否则,它获取当前内存块的大小,并将请求的大小对齐到最近的对齐大小的倍数,并加上基本节点结构的大小。接下来,它比较当前块的大小和请求的大小,如果当前块足够大,则调用Partition_Node函数进行分割。如果当前块不够大,则检查下一个相邻块是否为空闲且足够大,如果是,则将其合并到当前块中,并根据需要进行分割。如果下一个块也不满足条件,则调用TLSF_malloc函数分配一个新的块,并将原始数据复制到新块中,然后释放原始块。最后,返回指向重新分配内存的新地址。
extern void* TLSF_realloc(Memory_Head* Head, void* addr, Base_Type size)
{
if (Head == NULL) return NULL;
if (addr == NULL) return TLSF_malloc(Head, size);
if (size == 0) { TLSF_free(Head, addr); return NULL; }
List_Node* Node = (List_Node*)((BYTE*)addr - ALIGN_SIZE(sizeof(Base_Node), ALIGN_BYTE));
Base_Type Old_size = Node->Base.Block_Size;
List_Node* Next_Node = (List_Node*)((BYTE*)Node + Old_size);
size = ALIGN_SIZE(size, ALIGN_BYTE) + ALIGN_SIZE(sizeof(Base_Node), ALIGN_BYTE);
size = (size < BLOCK_Min_SIZE) ? ALIGN_SIZE(BLOCK_Min_SIZE, ALIGN_BYTE) : ALIGN_SIZE(size, ALIGN_BYTE);
if (Old_size >= size)
{//如果当前块足够大,则调用Partition_Node函数进行分割。
if (Old_size >= (size + BLOCK_Min_SIZE))
{//如果当前块足够大以容纳请求的大小加上最小块大小,则调用Partition_Node函数进行分割。
List_Node* Free_Node = (List_Node*)((BYTE*)Node + size);
Node->Base.Block_Size = size;
Free_Node->Base.Block_Size = Old_size - size, Free_Node->Base.Pre_addr = Node;
if (Next_Node->Base.Used_flag == 0)
{//如果下一个块是空闲的,则将其从自由列表中删除,并将其块大小加到当前块的块大小上,同时更新下一个节点的前一个地址指针。
List_Node* Next_Next_Node = (List_Node*)((BYTE*)Next_Node + Next_Node->Base.Block_Size);
Delete_Node(Head, Next_Node);
Free_Node->Base.Block_Size += Next_Node->Base.Block_Size;
Next_Next_Node->Base.Pre_addr = Free_Node;
}
else//如果下一个块不是空闲的,则直接将分割后的空闲块添加到自由列表中。
Next_Node->Base.Pre_addr = Free_Node;
Add_Node(Head, Free_Node);
}
}
else
{//如果当前块不够大,则检查下一个相邻块是否为空闲且足够大,如果是,则将其合并到当前块中,并根据需要进行分割。如果下一个块也不满足条件,则调用TLSF_malloc函数分配一个新的块,并将原始数据复制到新块中,然后释放原始块。最后,返回指向重新分配内存的新地址。
if (Next_Node->Base.Used_flag == 0 && (Next_Node->Base.Block_Size + Old_size) >= size)
{//如果下一个块是空闲的且足够大,则将其合并到当前块中,并根据需要进行分割。
List_Node* Next_Next_Node = (List_Node*)((BYTE*)Next_Node + Next_Node->Base.Block_Size);
Delete_Node(Head, Next_Node);
if (Next_Node->Base.Block_Size + Old_size >= (size + BLOCK_Min_SIZE))
{//如果合并后的块足够大,则调用Partition_Node函数进行分割。
List_Node* Divide_Node = (List_Node*)((BYTE*)Node + size);
Divide_Node->Base.Block_Size = Next_Node->Base.Block_Size - (size - Old_size), Divide_Node->Base.Pre_addr = Node;
Next_Next_Node->Base.Pre_addr = Divide_Node;
Node->Base.Block_Size = size;
Add_Node(Head, Divide_Node);
}
else
{//如果合并后的块不满足分割条件,则直接将其合并到当前块中。
Node->Base.Block_Size += Next_Node->Base.Block_Size;
Next_Next_Node->Base.Pre_addr = Node;
}
}
else
{//如果下一个块也不满足条件,则调用TLSF_malloc函数分配一个新的块,并将原始数据复制到新块中,然后释放原始块。最后,返回指向重新分配内存的新地址。
void* New_Node = TLSF_malloc(Head, size - ALIGN_SIZE(sizeof(Base_Node), ALIGN_BYTE));
if (New_Node == NULL) return NULL;
Memcpy_s(New_Node, addr, Old_size - ALIGN_SIZE(sizeof(Base_Node), ALIGN_BYTE));
TLSF_free(Head, addr);
addr = New_Node;
}
}
return addr;
}
// TLSF_calloc函数用于分配一块指定大小的内存,并将其初始化为零。它首先计算出总的内存大小,即请求的元素数量乘以每个元素的大小。然后,它调用TLSF_malloc函数进行分配,如果分配成功,则调用Memory_Reset函数将分配的内存块初始化为零。最后,返回指向分配内存的指针。
extern void* TLSF_calloc(Memory_Head* Head, Base_Type num, Base_Type size)
{
if (Head == NULL || num == 0 || size == 0) return NULL;
if (num > (0x0UL - 1) / size) return NULL; // 在乘法前检查溢出
Base_Type total_size = num * size;
void* ptr = TLSF_malloc(Head, total_size);
if (ptr) Memory_Reset(ptr, total_size);
return ptr;
}
// TLSF_malloc_Safe、TLSF_free_Safe、TLSF_realloc_Safe和TLSF_calloc_Safe函数是线程安全版本的内存分配函数。它们在调用相应的内存分配函数之前,首先检查内存管理器头中的Lock函数指针是否存在,如果存在,则调用Lock函数来获取锁,以确保在多线程环境下对内存管理器的访问是互斥的。然后,它调用相应的内存分配函数进行操作,并在操作完成后检查Unlock函数指针是否存在,如果存在,则调用Unlock函数来释放锁。最后,返回相应的结果。
extern void* TLSF_malloc_Safe(Memory_Head* Head, Base_Type size)
{
if (Head == NULL || size == 0) return NULL;
if (Head->Lock) Head->Lock();
Head->Condition = MALLOC_ING;
void* ptr = TLSF_malloc(Head, size);
if (Head->Unlock) Head->Unlock();
Head->Condition = IDLE;
return ptr;
}
// TLSF_free_Safe函数用于释放之前分配的内存块。它首先检查输入的内存管理器头指针和要释放的地址是否为NULL,如果是,则直接返回。否则,它调用Lock函数获取锁(如果存在),将内存管理器的状态设置为FREE_ING,调用TLSF_free函数进行释放操作,然后调用Unlock函数释放锁(如果存在),最后将内存管理器的状态设置回IDLE。
extern void TLSF_free_Safe(Memory_Head* Head, void* addr)
{
if (Head == NULL || addr == NULL) return;
if (Head->Lock) Head->Lock();
Head->Condition = FREE_ING;
TLSF_free(Head, addr);
if (Head->Unlock) Head->Unlock();
Head->Condition = IDLE;
}
// TLSF_realloc_Safe函数用于重新分配内存块。它首先检查输入的内存管理器头指针是否为NULL,如果是,则直接返回NULL。然后,它检查输入的地址是否为NULL,如果是,则调用TLSF_malloc_Safe函数进行分配。接下来,它检查请求的大小是否为零,如果是,则调用TLSF_free_Safe函数释放内存并返回NULL。否则,它调用TLSF_realloc函数进行重新分配,并在操作完成后返回相应的结果。
extern void* TLSF_realloc_Safe(Memory_Head* Head, void* addr, Base_Type size)
{
if (Head == NULL) return NULL;
if (Head->Lock) Head->Lock();
Head->Condition = REALLOC_ING;
void* ptr = TLSF_realloc(Head, addr, size);
if (Head->Unlock) Head->Unlock();
Head->Condition = IDLE;
return ptr;
}
// TLSF_calloc_Safe函数用于分配一块指定大小的内存,并将其初始化为零。它首先检查输入的内存管理器头指针是否为NULL,如果是,则直接返回NULL。然后,它检查请求的元素数量和每个元素的大小是否为零,如果是,则直接返回NULL。接下来,它调用Lock函数获取锁(如果存在),将内存管理器的状态设置为CALLOC_ING,调用TLSF_calloc函数进行分配和初始化操作,然后调用Unlock函数释放锁(如果存在),最后将内存管理器的状态设置回IDLE,并返回相应的结果。
extern void* TLSF_calloc_Safe(Memory_Head* Head, Base_Type num, Base_Type size)
{
if (Head == NULL || num == 0 || size == 0) return NULL;
if (Head->Lock) Head->Lock();
Head->Condition = CALLOC_ING;
void* ptr = TLSF_calloc(Head, num, size);
if (Head->Unlock) Head->Unlock();
Head->Condition = IDLE;
return ptr;
}
// Thread_Safe_TLSF_malloc、Thread_Safe_TLSF_free、Thread_Safe_TLSF_realloc和Thread_Safe_TLSF_calloc函数是线程安全版本的内存分配函数。它们在调用相应的线程安全内存分配函数之前,首先通过循环检查内存管理器头中的Condition字段是否为IDLE状态,以确保在多线程环境下对内存管理器的访问是互斥的。然后,它调用相应的线程安全内存分配函数进行操作,并在操作完成后返回相应的结果。
extern void* Thread_Safe_TLSF_malloc(Base_Type size)
{
Base_Type i = 0;
while (TLSF_Manager.Memory_Head[i% MEMORY_BLOCK]->Condition != IDLE) i++;
return TLSF_malloc_Safe(TLSF_Manager.Memory_Head[i], size);
}
// Thread_Safe_TLSF_free函数用于释放之前分配的内存块。它首先检查输入的内存管理器头指针和要释放的地址是否为NULL,如果是,则直接返回。否则,它调用Lock函数获取锁(如果存在),将内存管理器的状态设置为FREE_ING,调用TLSF_free函数进行释放操作,然后调用Unlock函数释放锁(如果存在),最后将内存管理器的状态设置回IDLE。
extern void Thread_Safe_TLSF_free(void* addr)
{
if (addr == NULL) return;
Base_Node* Node = (Base_Node*)((BYTE*)addr - ALIGN_SIZE(sizeof(Base_Node), ALIGN_BYTE));
TLSF_free_Safe(TLSF_Manager.Memory_Head[Node->Memory_flag],addr);
}
// Thread_Safe_TLSF_realloc函数用于重新分配内存块。它首先检查输入的内存管理器头指针是否为NULL,如果是,则直接返回NULL。然后,它检查输入的地址是否为NULL,如果是,则调用TLSF_malloc_Safe函数进行分配。接下来,它检查请求的大小是否为零,如果是,则调用TLSF_free_Safe函数释放内存并返回NULL。否则,它调用TLSF_realloc函数进行重新分配,并在操作完成后返回相应的结果。
extern void* Thread_Safe_TLSF_realloc(void* addr, Base_Type size)
{
if (addr == NULL) return Thread_Safe_TLSF_malloc(size);
Base_Node* Node = (Base_Node*)((BYTE*)addr - ALIGN_SIZE(sizeof(Base_Node), ALIGN_BYTE));
return TLSF_realloc_Safe(TLSF_Manager.Memory_Head[Node->Memory_flag], addr, size);
}
// Thread_Safe_TLSF_calloc函数用于分配一块指定大小的内存,并将其初始化为零。它首先检查输入的内存管理器头指针是否为NULL,如果是,则直接返回NULL。然后,它检查请求的元素数量和每个元素的大小是否为零,如果是,则直接返回NULL。接下来,它调用Lock函数获取锁(如果存在),将内存管理器的状态设置为CALLOC_ING,调用TLSF_calloc函数进行分配和初始化操作,然后调用Unlock函数释放锁(如果存在),最后将内存管理器的状态设置回IDLE,并返回相应的结果。
extern void* Thread_Safe_TLSF_calloc(Base_Type num, Base_Type size)
{
Base_Type i = 0;
while (TLSF_Manager.Memory_Head[i % MEMORY_BLOCK]->Condition != IDLE) i++;
return TLSF_calloc_Safe(TLSF_Manager.Memory_Head[i], num, size);
}
//原地扩展内存的大小
extern void Extend_Memory_Space(Memory_Head* Head, Base_Type size)
{
size &= ~(ALIGN_BYTE - 1);
if (Head == NULL || size == 0 || size < BLOCK_Min_SIZE) return;
List_Node* End_addr = Head->End_Addr_Node;
List_Node* End_Pre_addr = End_addr->Base.Pre_addr;
List_Node* Extend_Node = NULL;
//整理尾空间合并内存大小
if (!End_Pre_addr->Base.Used_flag)
{//如果靠近尾部最后一块内存是空闲状态
Extend_Node = End_Pre_addr;
Delete_Node(Head, End_Pre_addr);
Extend_Node->Base.Block_Size += End_addr->Base.Block_Size + size;
}
else
{//否则,就把尾部哨兵块合并整理
Extend_Node = End_addr;
Extend_Node->Base.Block_Size += size;
}
//重新标记好每个块的信息
End_addr = (List_Node*)((BYTE*)Extend_Node + (Extend_Node->Base.Block_Size - BLOCK_Min_SIZE));
End_addr->Base.Used_flag = 1, End_addr->Base.Block_Size = BLOCK_Min_SIZE, End_addr->Base.Pre_addr = Extend_Node, End_addr->Base.Memory_flag = Head->Memory_index;
Extend_Node->Base.Block_Size -= BLOCK_Min_SIZE, Extend_Node->Base.Memory_flag = Head->Memory_index;
Head->Total_Size += size;//更新管理块大小
Add_Node(Head, Extend_Node);
Head->End_Addr_Node = End_addr;
}
//尝试原地缩减管理的内存,返回值是缩减的内存字节
extern Base_Type Shrinkage_Memory(Memory_Head* Head, Base_Type Block_num, Base_Type Block_size)
{
Block_size &= ~(ALIGN_BYTE - 1);//保持缩减后内存对齐
if (Head == NULL || Block_num == 0 || Block_size == 0) return 0;
List_Node* End_addr = Head->End_Addr_Node;
List_Node* End_Pre_addr = End_addr->Base.Pre_addr;
if (End_Pre_addr->Base.Used_flag) return 0;//判断是否可以缩减内存
Base_Type Shrink_num = End_Pre_addr->Base.Block_Size / Block_size;//获取缩减后的余量
Shrink_num = Shrink_num < Block_num ? Shrink_num : Block_num;
Base_Type Remain = End_Pre_addr->Base.Block_Size - (Shrink_num * Block_size);
//如果余量过小,不足以支撑一个基础块的大小,则扩大余量,使剩余的余量支撑一个基础节点的大小
if (Remain && Remain < BLOCK_Min_SIZE)
{//如果余量过小
if (Shrink_num * Block_size < (BLOCK_Min_SIZE - Remain)) return 0;//如果余量过小,不足以支撑一个基础块的大小
Shrink_num = (End_Pre_addr->Base.Block_Size - BLOCK_Min_SIZE) / Block_size;//扩大余量,使剩余的余量支撑一个基础节点的大小
Remain = End_Pre_addr->Base.Block_Size - (Shrink_num * Block_size);
}
if (Shrink_num == 0) return 0;//如果没有缩减的内存块数量,则直接返回
Base_Type Reduct_byte = Shrink_num * Block_size;//计算缩减的内存字节数
Delete_Node(Head, End_Pre_addr);//取出内存块
End_Pre_addr->Base.Block_Size -= Reduct_byte, End_Pre_addr->Base.Memory_flag = Head->Memory_index;//缩减内存块的大小
End_addr = (List_Node*)((BYTE*)End_Pre_addr + End_Pre_addr->Base.Block_Size);//重新计算尾部哨兵块的位置
End_addr->Base.Used_flag = 1, End_addr->Base.Block_Size = BLOCK_Min_SIZE, End_addr->Base.Memory_flag = Head->Memory_index;//重新标记好每个块的信息
if (Remain) End_addr->Base.Pre_addr = End_Pre_addr, Add_Node(Head, End_Pre_addr);//如果余量不为0,则把缩减后的内存块添加回自由列表中
Head->End_Addr_Node = End_addr;
Head->Total_Size -= Reduct_byte;//更新管理块大小
return Reduct_byte;
}
// Thread_Extend_Memory_Space_Safe函数用于在线程安全的环境下扩展内存空间。它首先检查输入的内存管理器头指针和要扩展的大小是否有效,如果无效则直接返回。然后,它调用Lock函数获取锁(如果存在),调用Extend_Memory_Space函数进行内存扩展操作,然后调用Unlock函数释放锁(如果存在)。最后,返回相应的结果。
extern Thread_Extend_Memory_Space_Safe(Memory_Head* Head, Base_Type size)
{
if (Head == NULL || size == 0) return;
if (Head->Lock) Head->Lock();
Extend_Memory_Space(Head, size);
if (Head->Unlock) Head->Unlock();
}
// Thread_Shrinkage_Memory_Safe函数用于在线程安全的环境下尝试缩减内存空间。它首先检查输入的内存管理器头指针、要缩减的块数量和块大小是否有效,如果无效则直接返回0。然后,它调用Lock函数获取锁(如果存在),调用Shrinkage_Memory函数进行内存缩减操作,并将缩减的内存字节数保存在Shrink_byte变量中。最后,它调用Unlock函数释放锁(如果存在),并返回Shrink_byte。
extern Thread_Shrinkage_Memory_Safe(Memory_Head* Head, Base_Type Block_num, Base_Type Block_size)
{
if (Head == NULL || Block_num == 0 || Block_size == 0) return 0;
if (Head->Lock) Head->Lock();
Base_Type Shrink_byte = Shrinkage_Memory(Head, Block_num, Block_size);
if (Head->Unlock) Head->Unlock();
return Shrink_byte;
}
//初始化内存块
extern void Memory_Reset(void* addr, Base_Type Length)
{
BYTE* addr_byte = (BYTE*)addr;
long long* addr_int = NULL;
Base_Type Len = 0;
if (addr == NULL || Length == 0) return;
// 地址处理非对齐部分
for (; Length && ((Base_Type)addr_byte & (sizeof(long long) - 1)); Length--)
{
*addr_byte++ = 0;
}
addr_int = (long long*)addr_byte;
Len = Length / sizeof(long long);
for (; Len; Len--)
{
*addr_int++ = 0;
}
//剩余部分处理
Len = Length % sizeof(long long);
addr_byte = (BYTE*)addr_int;
for (; Len; Len--)
{
*addr_byte++ = 0;
}
}
//内存拷贝
extern void* Memcpy_s(void* destinction, void* orignal, Base_Type Length)
{//下面的代码未考虑内存对齐问题
BYTE* dest = (BYTE*)destinction;
BYTE* orign = (BYTE*)orignal;
long long* dest_int = NULL;
long long* orign_int = NULL;
Base_Type Len = Length % sizeof(long long);
if (!destinction || !orignal || Length == 0) return NULL;
if ((dest > orign) && ((orign + Length) > dest))
{
// 当目的地址大于源地址并且复制的长度大于源地址时
dest += Length;
orign += Length;
for (; Len; Len--)
{
*(--dest) = *(--orign);
}
Len = Length / sizeof(long long);
dest_int = (long long*)dest;
orign_int = (long long*)orign;
for (; Len; Len--)
{
*(--dest_int) = *(--orign_int);
}
}
else
{
for (; Len; Len--)
{
*dest++ = *orign++;
}
Len = Length / sizeof(long long);
dest_int = (long long*)dest;
orign_int = (long long*)orign;
for (; Len; Len--)
{
*dest_int++ = *orign_int++;
}
}
return destinction;
}
//获取当前管理的大小
extern Base_Type Get_Head_Total(Memory_Head* Head)
{
return Head == NULL ? 0 : Head->Total_Size;
}
// Get_Current_Available函数用于获取当前内存管理器中可用的内存大小。它直接返回内存管理器头中的Available_Size字段,该字段表示当前可用的内存大小。
extern Base_Type Get_Current_Available(Memory_Head* Head)
{
return Head == NULL ? 0 : Head->Available_Size;
}
// Get_Node_Size函数用于获取一个内存块节点的大小。它首先检查输入的地址是否为NULL,如果是,则返回0。否则,它通过将输入地址减去基本节点结构的大小来获取对应的List_Node结构,并返回该节点的块大小减去基本节点结构的大小,以得到实际可用的内存大小。
extern Base_Type Get_Node_Size(Memory_Head* Head, void* addr)
{
if (addr == NULL) return 0;
List_Node* Node = (List_Node*)((BYTE*)addr - ALIGN_SIZE(sizeof(Base_Node), ALIGN_BYTE));
return Node->Base.Block_Size - ALIGN_SIZE(sizeof(Base_Node), ALIGN_BYTE);
}
2、Test测试文件实现
c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "TLSF.h"
Memory_Head Malloc_Head = { 0 };
// 自定义内存管理系统的测试
void test_custom_malloc()
{
int** P = (int**)malloc(sizeof(int*) * 10000);
if (P == NULL) return;
// 进行1000 0000次申请操作
printf("Test Custom Malloc start!\n");
clock_t t = clock();
// 初始化指针数组
for (int i = 0; i < 10000; i++)
P[i] = NULL;
for (int i = 0; i < 10000000; i++)
{
if (rand() % 3 == 0 && P[i % 10000] == NULL)
{
unsigned int size = rand() % 1024 + 1; // 随机申请1到1024字节的内存
P[i % 10000] = (int*)Thread_Safe_TLSF_malloc(size);
if(P[i % 10000] != NULL) memset(P[i % 10000], 0, size);
}
else
{
Thread_Safe_TLSF_free(P[i % 10000]);
P[i % 10000] = NULL;
}
}
printf("Custom Memory expend %lu ms\n", (clock() - t) * 1000 / CLOCKS_PER_SEC);
// 指针数组归零
for (int i = 0; i < 10000; i++)
{
Thread_Safe_TLSF_free(P[i]);
P[i] = NULL;
}
free(P);
printf("Test Custom Malloc Finish!\n\n");
}
// 标准库 malloc 的测试
void test_standard_malloc()
{
int** P = (int**)malloc(sizeof(int*) * 10000);
if (P == NULL) return;
// 进行1000 0000次申请操作
printf("Test Standard Malloc start!\n");
clock_t t = clock();
// 初始化指针数组
for (int i = 0; i < 10000; i++)
P[i] = NULL;
for (int i = 0; i < 10000000; i++)
{
if (rand() % 3 == 0 && P[i % 10000] == NULL)
{
unsigned int size = rand() % 1024 + 1; // 随机申请1到1024字节的内存
P[i % 10000] = (int*)malloc(size);
if (P[i % 10000] != NULL) memset(P[i % 10000], 0, size);
}
else
{
free(P[i % 10000]);
P[i % 10000] = NULL;
}
}
printf("Standard Library malloc expend %lu ms\n", (clock() - t) * 1000 / CLOCKS_PER_SEC);
// 指针数组归零
for (int i = 0; i < 10000; i++)
{
free(P[i]);
P[i] = NULL;
}
free(P);
printf("Test Standard Malloc Finish!\n\n");
}
// 测试函数
int main(void)
{
srand(time(NULL));
if (!Init_TLSF(&Malloc_Head, malloc(80000 * 10240), 80000 * 10240, NULL, NULL)) return;
printf("Init Memory Available is %u Byte\n", Get_Current_Available(&Malloc_Head));
test_custom_malloc();
printf("Custom Memory Available is %u Byte\n", Get_Current_Available(&Malloc_Head));
test_standard_malloc();
system("pause");
return 0;
}
九、适用场景与局限性
9.1 最佳适用场景
- 实时操作系统(RTOS):需要确定性延迟的内存分配
- 嵌入式/物联网设备:资源受限,对碎片敏感
- 游戏引擎:高频、小粒度的内存分配/释放
- 音视频处理:帧缓冲区的动态管理
- 高并发服务器:需要低延迟的内存操作
9.2 局限性
- 无内存压缩能力:TLSF可以合并相邻空闲块,但无法移动已分配块来整理碎片。在极端碎片场景下,可能出现"总空闲内存足够但无法分配大块连续内存"的情况。
- 线程安全开销:虽然提供了线程安全版本,但锁竞争仍会带来性能损耗。对于极高并发场景,可能需要结合线程局部存储(TLS)进一步优化。
- 内存池粒度:TLSF的管理开销与内存池大小成正比,对于极小的内存池(<1KB),管理开销占比可能较高。
十、总结
TLSF内存分配器通过两级位图索引 、即时合并策略 和确定性时间复杂度,为实时系统和嵌入式设备提供了一种工业级的内存管理方案。本文分析的这份实现代码,在标准TLSF的基础上进一步增强了:
- ✅ 多内存池管理:支持多个独立内存区域
- ✅ 线程安全机制:三层安全策略,可插拔锁实现
- ✅ 动态扩展/缩减:运行时调整内存池大小
- ✅ 跨平台适配:16/32/64位系统兼容,GCC/非GCC编译器适配
- ✅ 工程级细节:内存重叠检测、对齐优化、位域压缩
如果你正在开发一个对内存延迟敏感的项目,或者想深入理解内存分配器的底层原理,TLSF无疑是一个值得深入研究和应用的算法。