Skip to content

MemoryDealer内存分配器

追踪 MemoryDealer 的共享堆、子块分配、best-fit 链表、MemoryBase Binder 对象和释放合并。

基于android-17.0.0_r1
AndroidBinderMemoryDealer共享内存

MemoryDealer内存分配器 ​

MemoryDealer 把一个共享堆拆成可复用的 MemoryBase 子块;分配、释放和 Binder 传递的 owner 不同,不能把子块析构误认为共享 fd 已关闭。

本文面向已读过 Ashmem共享内存 和 Binder one-copy 的读者。MemoryDealer 是用户态对一个共享 heap 的二级分配器:它先创建并映射整块 MemoryHeapBase,再把偏移/长度切成多个 MemoryBase 子对象通过 Binder 共享。它不管理 Binder transaction buffer,也不替代内核 allocator。

1. 堆与分配器 ​

源码文件:frameworks/native/libs/binder/MemoryDealer.cpp

cpp
MemoryDealer::MemoryDealer(size_t size, const char* name, uint32_t flags)
      : mHeap(sp<MemoryHeapBase>::make(size, flags, name)),
        mAllocator(new SimpleBestFitAllocator(size)) {}

mHeap 是真正的 Ashmem/memfd 映射,mAllocator 只记录该 heap 内的逻辑区间。两者 size 都按页对齐,但 allocator 额外以 32 字节 cache-line 边界分配子块。

2. 子块对象 ​

源码文件:frameworks/native/libs/binder/MemoryBase.cpp

MemoryBase 保存 sp<IMemoryHeap>、mOffset、mSize;getMemory 只返回 heap 和区间,不复制数据。MemoryDealer::allocate 将 offset/size 包装为内部 Allocation,客户端拿到的是一个描述子区间的 Binder 对象。

3. Best-fit分配 ​

源码文件:frameworks/native/libs/binder/MemoryDealer.cpp

SimpleBestFitAllocator::alloc 在双向 chunk 链表中寻找最小可容纳 free chunk。请求按 32 字节单位向上取整;PAGE_ALIGNED 会增加前置 padding,使返回 offset 对齐页边界。找到后可能分裂前置 padding 和尾部 free chunk。

cpp
if (cur->free && cur->size >= (size + extra)) {
    if (!free_chunk || cur->size < free_chunk->size)
        free_chunk = cur;
}

best-fit 减少大洞被小请求消耗,但仍会产生碎片;没有单个连续 chunk 时返回 NO_MEMORY,即使链表 free 总量足够。

4. 分配锁 ​

SimpleBestFitAllocator::allocate/deallocate/dump 都使用 mLock。锁只保护用户态 chunk 链表;heap 映射和 Binder 事务由其他对象/线程管理。把 MemoryDealer 锁与服务业务锁一起跨 IPC 持有,会形成用户态死锁风险。

5. 释放合并 ​

源码文件:frameworks/native/libs/binder/MemoryDealer.cpp

Allocation 析构时读取自身 offset/size,调用 mDealer->deallocate(offset)。allocator 找到起始 chunk,标记 free,并把相邻 free chunk 合并。size 为 0 的 Allocation 特殊处理:它没有 allocator 记录,offset 可能为 0,不能进入 deallocate。

6. 生命周期 ​

Allocation 持有 sp<MemoryDealer>,因此即使外部 dealer 引用释放,heap 和 allocator 仍保持到最后一个子块析构。反过来,如果客户端只保存裸 offset 而丢失 IMemory 强引用,子块可能提前释放并让该区间重新分配给其他使用者。

7. Binder共享 ​

MemoryBase 通过生成的 IMemory Binder 接口让远端获取 heap、offset、size;Binder 传递的是 heap 的 fd/对象和区间元数据,不是子块 payload。接收端映射同一 heap 后加上 offset 访问数据,多个子块可以共享一个 fd 和页表 backing。

8. 对齐差异 ​

MemoryDealer 的 32 字节对齐服务于 cache line;MemoryHeapBase 以 page 对齐创建映射;Binder Parcel/allocator 又有 pointer-size 对齐。不要用某一层的 offset 对齐假设替代其他层:传入 PAGE_ALIGNED 只影响 allocator 返回子块,不能改变 heap 起始地址或 Binder buffer 对齐。

9. 后端与保护 ​

源码文件:frameworks/native/libs/binder/MemoryHeapBase.cpp

MemoryHeapBase 可根据 flags 使用 Ashmem 或强制 memfd;READ_ONLY 会设置 Ashmem protection 或 memfd sealing。MemoryDealer 只接收 heap,不重新定义 fd 权限;子块读写能力受底层映射和 fd seal 共同限制。

10. 测试与模糊输入 ​

源码文件:frameworks/native/libs/binder/tests/unit_fuzzers/MemoryDealerFuzz.cpp

fuzzer 反复生成 allocate/deallocate/dump 操作,覆盖随机 size、offset 和释放顺序,目标是检查链表合并、重复释放和边界输入不会破坏 allocator。它验证的是用户态分配器健壮性,不证明跨进程客户端能安全使用悬空 IMemory。

11. 对照内核 ​

MemoryDealer:用户态、一个共享 heap 内的逻辑子块、mLock、32 字节对齐;Binder allocator:内核态、每个进程 transaction buffer、alloc->mutex、pointer 对齐和 async quota。两者都使用 best-fit/合并思想,但 owner、生命周期、错误码和传输目的完全不同。

12. 阅读检查 ​

从 MemoryDealer::allocate 追到 MemoryBase::getMemory,说明数据为什么没有复制;再给出“free 总量足够但连续 chunk 不足”“Allocation 析构后仍使用裸指针”“READ_ONLY memfd”三种结果,分别定位 allocator、引用生命周期和 fd seal 的源码。