Page 505 - 《软件学报》2024年第4期
P. 505
欧阳湘臻 等: 榫卯: 一种可组合的定制化内存分配框架 2083
策略 P x :=func A 层级连接 策略槽 P i
基础层 L n 基础层 L n+1
策略槽 P j 策略槽 P k
策略 P y :=func B
图 2 榫卯内存框架层级组合示意图
综上所述, 本文从函数式编程视角出发定义内存分配器框架主要出于以下考量. 可组合性: 函数式编程允许使
用高阶复合函数组合现有的函数来产生新函数, 与面向对象编程中对类进行组合相比, 其组合粒度更低. 简洁性:
函数式编程允许使用更简洁, 抽象的函数模型语言描述或构造复杂的内存分配器. 验证友好: 层级函数的输出只与
其输入参数有关, 状态和副作用均被封装到内存请求上下文中, 因而更易于测试与验证.
2.2 榫卯框架的实现细节
本文基于标准 C 实现了榫卯内存分配框架. 榫卯框架尽可能将实现的复杂性转移到框架本身, 对于开发者而
言只需先引入已有层级, 使用策略填充该层, 并最终将各层连接起来即可, 如图 3 所示的内存分配器构建示例. 在
榫卯内存分配框架下, 开发者仅需要数十行代码便可组合出一个复杂的, 带有尾部校验和检测及错误处理功能的
3 层架构的 malloc(3) 高性能内存分配器. 图 3 中构建的内存分配器使用抽象模型语言可以表示为: mmap_
heap(faa_heap_growth=1, threadlocal_heap_size=1GB)::buddy_glk(sync=spinlock, size_handle=[65536,
SIZE_MAX))::slab_wf(sizeclass=prime, size_handle=[0, 65536))::tail_chksum(chksum=xor)::malloc_socket.
内存请求示意 层级连接 同步策略 // 引入错误处理策略
策略填充 #include "policy/error/def_handle.h"
mutex // 引入supermalloc的大小阶策略
#include "policy/sizeclass/prime.h"
// 引入spinlock同步策略
spinlock #include "policy/sync/spinlock.h"
系统接口层 mmap_heap // 引入异或校验策略
#include "policy/chksum/xor.h"
...
// 完成层级的定制
... // 定制tail_chksum层用于检查缓冲区溢出
// 使用默认的错误处理和异或校验
USE_ERRHANDLE(tail_chksum, default)
USE_CHKSUM(tail_chksum, xor)
大小阶划分策略 #include "layer/block/tail_chksum.h"
buddy_glk
... // 定制slab_wf层,处理0~65536字节的内存请求
SIZE_HANDLE(slab_wf, 0, 65536)
USE_SIZECLASS(slab_wf, prime)
log2b #include "layer/block/slab_wf.h"
基础层 // 定制buddy_glk层,处理65536字节以上的内存请求
wfslab prime
SIZE_HANDLE(buddy_glk, 65536, SIZE_MAX)
USE_SYNCH(buddy_glk, spinlock)
... #include "layer/block/buddy_glk.h"
// 定制mmapheap层,使用原子加指令进行堆增长
[65 536, // 设置线程一次性申请的虚拟内存大小
SIZE_MAX) USE_FAA_HEAP(1)
tail_chksum 校验策略
USE_THREADLOCAL_HEAP_SIZE(1 << 30)
#include "layer/os/mmapheap.h"
...
// 完成分配器的整体定义
[0, 65 536) // 定义系统接口层
xor
DEFINE_OSLAYER(mmapheap)
... // 连接各层完成复合
用户接口层 malloc_socket LAYER_COMPOSE(mmapheap, buddy_glk, slab_wf,
tail_chksum, malloc_socket)
图 3 使用榫卯框架构建动态内存分配器