Android Ashmem 机制

Ashmem: Anonymous Shared MemoryAndroid 提供的一种共享内存的机制,它基于 mmap 系统调用,不同进程可以将同一段物理内存映射到各自的虚拟地址控制实现共享,因此进程间不需要再拷贝数据。

特点

Android 系统提供了独特的匿名共享内存子系统 Ashmem,它以驱动程序的形式实现在内核空间中,有两个典型特点:

  • 能够辅助内存管理系统来有效地管理不再使用的内存块
  • 通过 Binder 进程间通信机制来实现进程间的内存共享

Linux 共享内存通信效率非常高,进程间不需要传递数据,便可以直接访问,缺点也很明显,Linux 共享内存没有提供同步的机制,在使用时要借助其他的手段来处理进程间同步。所以在 Android 系统中实现的匿名共享内存 Ashmem 驱动中添加了互斥锁,另外通过传递文件描述符来实现共享内存的传递。

源码速查表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Framework:
frameworks/base/core/java/android/os/MemoryFile.java
frameworks/base/core/jni/android_os_MemoryFile.cpp

Native:
frameworks/native/include/binder/IMemory.h
frameworks/native/libs/binder/IMemory.cpp
frameworks/native/include/binder/MemoryHeapBase.h
frameworks/native/libs/binder/MemoryHeapBase.cpp
frameworks/native/include/binder/MemoryBase.h
frameworks/native/libs/binder/MemoryBase.cpp
frameworks/native/include/binder/MemoryDealer.h
frameworks/native/libs/binder/MemoryDealer.cpp

System:
system/core/libcutils/ashmem-host.c 用户空间 API 仿真
system/core/libcutils/ashmem-dev.c 真实实现
system/core/include/utils/ashmem.h
system/core/include/cutils/ashmem.h

Driver:
kernel/msm-3.18/drivers/staging/android/ashmem.c
kernel/msm-3.18/drivers/staging/android/ashmem.h
kernel/msm-3.18/include/uapi/linux/ashmem.h
kernel/msm-3.18/include/linux/ashmem.h

System

函数列表

Ashmem 匿名共享内存,全都是通过 System 层和 Driver 层交互的。函数列表:

1
2
3
4
5
6
7
8
9
10
11
// ashmem.h
// 根据名称和大小,创建 ashmem 区域,返回文件描述符
int ashmem_create_region(const char *name, size_t size);
// 设置 ashmem 访问保护位
int ashmem_set_prot_region(int fd, int prot);
// ashmem 锁定
int ashmem_pin_region(int fd, size_t offset, size_t len);
// ashmem 解锁
int ashmem_unpin_region(int fd, size_t offset, size_t len);
// ashmem 区域的大小
int ashmem_get_size_region(int fd);

使用到的宏

函数列表中值使用到了这些 ioctl 命令宏:

1
2
3
4
5
6
7
#define __ASHMEMIOC		0x77
#define ASHMEM_SET_NAME _IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN])
#define ASHMEM_SET_SIZE _IOW(__ASHMEMIOC, 3, size_t)
#define ASHMEM_GET_SIZE _IO(__ASHMEMIOC, 4)
#define ASHMEM_SET_PROT_MASK _IOW(__ASHMEMIOC, 5, unsigned long)
#define ASHMEM_PIN _IO(__ASHMEMIOC, 7)
#define ASHMEM_UNPIN _IO(__ASHMEMIOC, 8)

驱动

文件路径

1
2
3
4
./drivers/staging/android/ashmem.c
./drivers/staging/android/ashmem.h
./include/uapi/linux/ashmem.h
./include/linux/ashmem.h

Ashmem 结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
* The lifecycle of this structure is from our parent file's open() until
* its release(). It is also protected by 'ashmem_mutex'
* Warning: Mappings do NOT pin this structure; It dies on close()
*/
struct ashmem_area {
char name[ASHMEM_FULL_NAME_LEN]; /* optional name in /proc/pid/maps */
struct list_head unpinned_list; /* list of all ashmem areas */
struct file *file; /* the shmem-based backing file */
size_t size; /* size of the mapping, in bytes */
unsigned long vm_start; /* Start address of vm_area
* which maps this ashmem */
unsigned long prot_mask; /* allowed prot bits, as vm_flags */
};

// 匿名共享设备名
#define ASHMEM_DEVICE "/dev/ashmem"
  • name
    表示这块共享内存的名字,这个名字会显示 /proc/<pid>/maps 文件中,<pid> 表示打开这个共享内存文件的进程 ID
  • unpinned_list
    是一个列表头,它把这块共享内存中所有被解锁的内存块连接在一起,和内存块的锁定和解锁操作有关。
  • file
    表示这个共享内存在临时文件系统 tmpfs 中对应的文件,在内核决定要把这块共享内存对应的物理页面回收时,就会把它的内容交换到这个临时文件中去。
  • size
    表示这块共享内存的大小。
  • prot_mask
    表示这块共享内存的访问保护位。

关键函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 打开 Ashmem 设备
static int ashmem_open(struct inode *inode, struct file *file) {...};

// 响应 ioctl 相关命令
static long ashmem_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
...
switch (cmd) {
case ASHMEM_SET_NAME:
ret = set_name(asma, (void __user *) arg);
break;
case ASHMEM_GET_NAME:
ret = get_name(asma, (void __user *) arg);
break;
case ASHMEM_SET_SIZE:
...
}

// mmap 系统调用,内存映射,起始地址保存到 vm_start 中
static int ashmem_mmap(struct file *file, struct vm_area_struct *vma);

匿名共享内存文件进行内存映射操作,对匿名内存文件内容的读写操作就像访问内存变量一样,驱动不用参与到读写操作中。

pin/unpin 锁定和解锁内存区

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

struct ashmem_pin {
__u32 offset; /* offset into region, in bytes, page-aligned */
__u32 len; /* length forward from offset, in bytes, page-aligned */
};

/**
* struct ashmem_range - A range of unpinned/evictable pages
* @lru: The entry in the LRU list
* @unpinned: The entry in its area's unpinned list
* @asma: The associated anonymous shared memory area.
* @pgstart: The starting page (inclusive)
* @pgend: The ending page (inclusive)
* @purged: The purge status (ASHMEM_NOT or ASHMEM_WAS_PURGED)
*
* The lifecycle of this structure is from unpin to pin.
* It is protected by 'ashmem_mutex'
*/
struct ashmem_range {
struct list_head lru;
struct list_head unpinned;
struct ashmem_area *asma;
size_t pgstart;
size_t pgend;
unsigned int purged;
};


static int ashmem_pin_unpin(struct ashmem_area *asma, unsigned long cmd,
void __user *p);
// pin the given ashmem region, returning whether it was
static int ashmem_pin(struct ashmem_area *asma, size_t pgstart, size_t pgend);
// unpin the given range of pages. Returns zero on success.
static int ashmem_unpin(struct ashmem_area *asma,size_t pgstart,size_t pgend);

匿名共享内存的锁定和解锁操作,表示哪些内存块是正在使用的,需要锁定(pin);哪些内存是不需要使用了,解除锁定(unpin)。创建匿名共享内存时,默认所有的内存都是 pinned 状态的,用户先告诉 Ashmem 驱动程序要解锁某一块内存,内核可以将它对应的物理页面回收;因为 unpin 操作并不会改变已经 mmap 的地址空间,所以之后用户可以再告诉驱动程序要重新锁定某一块之前被解锁过的内块,从而修改这块内存的状态。也就是说,执行锁定前,目标对象必须是一块当前处于解锁状态的内存块。
匿名共享内存 Ashmem 机制是建立在 Linux 内核实现的共享内存的基础上的。同时它又向 Linux 内存管理系统的内存回收算法注册接口,系统内存不足时,会回收 Ashmem 区域中状态是 unpin 的对象内存块,如果不希望对象被回收,可以通过 pin 来保护它。

Java 层接口及应用

MemoryFile

所有 Java 代码都是通过 MemoryFile 来使用匿名共享内存 Ashmem 的,它是唯一的入口。
不过在 Andoid O 开始匿名共享内存推荐使用 SharedMemoryApiApplications should generally prefer to use SharedMemory which offers more flexible access & control over the shared memory region than MemoryFile does.

重要字段:

1
2
3
4
private FileDescriptor mFD;        // ashmem file descriptor
// mAddress存了驱动中 ashmem_open 返回虚拟地址空间的起始地址
private long mAddress; // address of ashmem memory
private int mLength; // total length of our ashmem region

调用流程

App --> (Framework) MemoryFile.java --> (Native) android_os_MemoryFile.cpp --> (System) ashmem-dev.c --> (Driver)ashmem.c

0043-MemoryFile-open-sequence-diag.png

序列图中可以看到,Java 接口文件直接通过 JNI 调用了系统 System 的函数,通过它来和驱动做数据交互。

如何使用

思路:通过 Binder 机制,传递匿名共享机制文件描述符,通过该描述符实现进程间的数据共享。
Linux 系统中,文件描述符其实就是一个整数。每一个进程在内核空间都有一个打开文件的数组,这个文件描述符的整数值就是用来索引这个数组的,而且这个文件描述符只是在本进程内有效,也就是说,在不同的进程中,相同的文件描述符的值,代表的可能是不同的打开文件。因此在进程间传输文件描述符时,并不是简要把一个文件描述符从一个进程传给另外一个进程,中间通过 Binder 机制做过转换,映射了相同的地址空间,使得它和源进程的文件描述符所对应的打开文件是一致的,这样才能保证共享。文件描述符进程间转换图:

0043-ashmem-fd.jpg

  • AIDL 文件
    通过 Binder 机制,传递匿名共享机制文件描述符。

    1
    2
    3
    interface IAshmemFd {
    ParcelFileDescriptor getParcelFileDescriptor();
    }
  • Server 服务端
    利用 Ashmem 机制,创建 MemoryFile 文件后,写入数据。注意:这里需要使用反射机制调用 getFileDescriptor 因为它是 @hide,也就是说,Android 系统并不推荐这种使用方式。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    private ParcelFileDescriptor getParcelFileDescriptor(){
    Log.d(TAG, "getParcelFileDescriptor: ");
    ParcelFileDescriptor pfd = null;
    try {
    // method 1: Ashmem: MemoryFile.
    MemoryFile file =new MemoryFile(ASHMEM_FILENAME,ASHMEM_LENGTH);
    file.getOutputStream().write(testData);
    Method method = MemoryFile.class
    .getDeclaredMethod("getFileDescriptor");
    FileDescriptor des = (FileDescriptor) method.invoke(file);
    pfd = ParcelFileDescriptor.dup(des);
    } catch (Exception e){
    Log.e(TAG, "getParcelFileDescriptor: ", e);
    }
    return pfd;
    }

    private IBinder mBinder = new IAshmemFd.Stub() {
    @Override
    public ParcelFileDescriptor getParcelFileDescriptor()
    throws RemoteException {
    return AshmemService.this.getParcelFileDescriptor();
    }
    };
  • Client 客户端
    客户端通过 Binder 拿到 ParcelFileDescriptor 文件描述符后,读取 MemoryFile 文件的内容并打印。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ParcelFileDescriptor pfd = mIAshmemFd.getParcelFileDescriptor();
    FileInputStream fileInputStream =
    new FileInputStream(pfd.getFileDescriptor());
    byte[] content = new byte[10];
    fileInputStream.read(content);
    StringBuilder builder = new StringBuilder();
    for (byte b : content){
    builder.append(b);
    }
    String value = builder.toString();
    Log.d(TAG, "getData: value = " + value);

总结

Android O 之后推荐使用 SharedMemory,之前的版本也几乎没有看到 MemoryFile 的应用。网上搜到的案例是 AIDL 传递文件描述符 ParcelFileDescriptor,而仅仅传递它其实并不需要使用 Ashmem,因为 ParcelFileDescriptor 中推荐直接使用 createPipe 创建管道。
Server 服务端使用管道实现的示例:

1
2
3
4
5
6
7
8
9
// method 2: Pipe
ParcelFileDescriptor[] pfds = ParcelFileDescriptor.createPipe();
// 第二个为写管道
OutputStream outputStream =
new ParcelFileDescriptor.AutoCloseOutputStream(pfds[1]);
outputStream.write(testData);
// 第一个为读管道
pfd = pfds[0];
// 将读管道文件描述符传递给客户端

Native 层接口及应用

类图结构

1
2
3
4
IMemory.h: IMemoryHeap, BnMemoryHeap, IMemory, BnMemory
IMemory.cpp: BpMemoryHeap, BpMemory
MemoryBase.h: MemoryBase
MemoryHeapBase.h: MemoryHeapBase

0043-Ashmem-class-uml.png

可以看出,Native 层的 Ashmem 本身就是基于 Binder 机制的。

  • MemoryHeapBase
    用于在进程间共享一个完整的匿名共享内存块。
  • MemoryBase
    用于在进程间共享一个匿名共享内存块中其中的一部分。 MemoryBase 接口是建立在 MemoryHeapBase 接口的基础上面的,它们都可以作为一个 Binder 对象来在进程间传输。

IMemoryHeap 定义的重要方法

1
2
3
4
5
6
// 获得匿名共享内存块的文件描述符
virtual int getHeapID() const = 0;
// 获得匿名共享内存块的基地址
virtual void* getBase() const = 0;
// 获得匿名共享内存块的大小
virtual size_t getSize() const = 0;

MemoryHeapBase 服务端

MemoryHeapBase 继承了 BnMemoryHeap,是 IMemoryHeap 的本地实现类,实现了具体的函数功能,用于服务端。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name)
: mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
mDevice(0), mNeedUnmap(false), mOffset(0)
{
const size_t pagesize = getpagesize();
size = ((size + pagesize-1) & ~(pagesize-1));
// 1. 创建匿名共享内存区域
int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size);
ALOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno));
if (fd >= 0) {
// 映射地址空间
if (mapfd(fd, size) == NO_ERROR) {
if (flags & READ_ONLY) {
// 设置保护区
ashmem_set_prot_region(fd, PROT_READ);
}
}
}
}

// 映射 mFD, mBase, mSize
status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset)
{
if (size == 0) {
// try to figure out the size automatically
struct stat sb;
if (fstat(fd, &sb) == 0)
size = sb.st_size;
// if it didn't work, let mmap() fail.
}

if ((mFlags & DONT_MAP_LOCALLY) == 0) {
void* base = (uint8_t*)mmap(0, size,
PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);
...
mBase = base;
mNeedUnmap = true;
} else {
mBase = 0; // not MAP_FAILED
mNeedUnmap = false;
}
mFD = fd;
mSize = size;
mOffset = offset;
return NO_ERROR;
}

int MemoryHeapBase::getHeapID() const {
return mFD;
}
void* MemoryHeapBase::getBase() const {
return mBase;
}
size_t MemoryHeapBase::getSize() const {
return mSize;
}
  • mFD:匿名共享内存文件描述符
  • mBase:匿名共享内存起始地址
  • mSize:匿名共享内存的大小

0043-MemoryHeapBase-sequence-diag.png

HeapCache 缓存

HeapCache 继承了 IBinder::DeathRecipient,维护生命周期。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class HeapCache : public IBinder::DeathRecipient
{
public:
...
virtual void binderDied(const wp<IBinder>& who);
sp<IMemoryHeap> find_heap(const sp<IBinder>& binder);
sp<IMemoryHeap> get_heap(const sp<IBinder>& binder);
void free_heap(const sp<IBinder>& binder);
...

private:
// For IMemory.cpp
struct heap_info_t {
sp<IMemoryHeap> heap;
int32_t count;
};
...
KeyedVector< wp<IBinder>, heap_info_t > mHeapCache;
};

static sp<HeapCache> gHeapCache = new HeapCache();
  • heap_info_t 结构体
    heap 保存了 BpMemoryHeap 对象;count 引用计数,表示被引用了多少次,只有在等于 1 时,才能被释放。
  • mHeapCache 缓存
    KeyedVector 容器类型,存储了以 IBinderheap_info_t 的键值对。
  • find_heap 查找
    mHeapCache 中查找,如果找不到则添加到 mHeapCache 中。
  • get_heap 获取
    mHeapCache 中查找,如果找不到则直接将 IBinder 转换,并没有加入 mHeapCache 中。
  • gHeapCache 全局实例
    gHeapCache 全局实例,维护了本进程中所有的 BpMemoryHeap 引用对象。

BpMemoryHeap 客户端

BpMemoryHeapIMemoryHeap 的代理类,用于客户端。Binder 机制中客户端通过代理类访问服务端的具体实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
virtual int getHeapID() const;
virtual void* getBase() const;
virtual size_t getSize() const;
// 匿名共享内存文件描述符
mutable volatile int32_t mHeapId;
mutable void* mBase;
mutable size_t mSize;

int BpMemoryHeap::getHeapID() const {
assertMapped();
return mHeapId;
}

void* BpMemoryHeap::getBase() const {
assertMapped();
return mBase;
}

size_t BpMemoryHeap::getSize() const {
assertMapped();
return mSize;
}

// 在获取匿名共享内存前,都会先确保映射成功
void BpMemoryHeap::assertMapped() const
{
if (mHeapId == -1) {
sp<IBinder> binder(IInterface::asBinder(const_cast<BpMemoryHeap*>(this)));
sp<BpMemoryHeap> heap(static_cast<BpMemoryHeap*>(find_heap(binder).get()));
heap->assertReallyMapped();
if (heap->mBase != MAP_FAILED) {
Mutex::Autolock _l(mLock);
if (mHeapId == -1) {
mBase = heap->mBase;
mSize = heap->mSize;
mOffset = heap->mOffset;
android_atomic_write( dup( heap->mHeapId ), &mHeapId );
}
} else {
// something went wrong
free_heap(binder);
}
}
}

void BpMemoryHeap::assertReallyMapped() const
{
if (mHeapId == -1) {
...
// Binder 调用,从 MemoryHeapBase 中拿到匿名共享内存相关信息
Parcel data, reply;
data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor());
status_t err = remote()->transact(HEAP_ID, data, &reply);
int parcel_fd = reply.readFileDescriptor();
ssize_t size = reply.readInt32();
uint32_t flags = reply.readInt32();
uint32_t offset = reply.readInt32();
...
if (mHeapId == -1) {
int fd = dup( parcel_fd );
...
mRealHeap = true;
// 将服务端的匿名共享内存映射到当前进程,并设置基地址
mBase = mmap(0, size, access, MAP_SHARED, fd, offset);
if (mBase == MAP_FAILED) {
ALOGE("cannot map BpMemoryHeap (binder=%p), size=%zd, fd=%d (%s)",
IInterface::asBinder(this).get(), size, fd, strerror(errno));
close(fd);
} else {
// 赋值匿名共享内存大小,文件描述符
mSize = size;
mFlags = flags;
mOffset = offset;
android_atomic_write(fd, &mHeapId);
}
}
}
}

在获取当前匿名共享内存信息时,都会先执行 assertMapped/assertReallyMapped 断言函数,确保获取到服务端的匿名共享内存信息后并映射到了当前进程。

IMemory 的定义

IMemory 是用来管理进程间匿名共享内存块 IMemoryHeap 中的一部分,所以这个类中保存了 IMemoryHeap 代理实例,以及被管理的匿名共享内存这部分的基地址,大小,在整个 Ashmem 中的偏移量。

  • 头文件定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class IMemory : public IInterface
    {
    public:
    ...
    // 获取匿名共享内存客户端代理实例 BpMemoryHeap
    virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const = 0;
    void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const;
    // 获取匿名共享内存的基地址
    void* pointer() const;
    // 获取匿名共享内存的大小
    size_t size() const;
    // 维护的这部分共享内存,在整个匿名共享内存中的偏移量
    ssize_t offset() const;
    };
  • 具体实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    // 从缓存中快速获取被管理的 Ashmem 基地址:MemoryHeap的基地址 + 偏移量
    void* IMemory::fastPointer(const sp<IBinder>& binder, ssize_t offset) const
    {
    sp<IMemoryHeap> realHeap = BpMemoryHeap::get_heap(binder);
    void* const base = realHeap->base();
    if (base == MAP_FAILED)
    return 0;
    return static_cast<char*>(base) + offset;
    }
    // 从具体实现类中获取被管理的 Ashmem 基地址:MemoryHeap的基地址 + 偏移量
    void* IMemory::pointer() const {
    ssize_t offset;
    sp<IMemoryHeap> heap = getMemory(&offset);
    void* const base = heap!=0 ? heap->base() : MAP_FAILED;
    if (base == MAP_FAILED)
    return 0;
    return static_cast<char*>(base) + offset;
    }
    // 被管理 Ashmem 的大小
    size_t IMemory::size() const {
    size_t size;
    getMemory(NULL, &size);
    return size;
    }
    // 被管理 Ashmem 的偏移量
    ssize_t IMemory::offset() const {
    ssize_t offset;
    getMemory(&offset);
    return offset;
    }

MemoryBase 服务端

MemoryBase 继承 BpMemory,是 IMemory 的本地实现类,用于服务端。

1
2
3
4
5
6
7
8
9
10
11
12
MemoryBase::MemoryBase(const sp<IMemoryHeap>& heap,
ssize_t offset, size_t size)
: mSize(size), mOffset(offset), mHeap(heap)
{
}
// 更新偏移量,大小,并返回 mHeap
sp<IMemoryHeap> MemoryBase::getMemory(ssize_t* offset, size_t* size) const
{
if (offset) *offset = mOffset;
if (size) *size = mSize;
return mHeap;
}

MemoryBase 类非常简单,构造函数中传入了匿名共享内存的具体实现 MemoryHeapBase 对象,以及被管理部分的偏移量和大小。getMemory 简单的返回了整个匿名共享内存对象及对应的偏移量和大小,所以可以理解 MemoryBaseMemoryHeapBase 的简单封装。

BpMemory 客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
sp<IMemoryHeap> BpMemory::getMemory(ssize_t* offset, size_t* size) const
{
if (mHeap == 0) {
Parcel data, reply;
data.writeInterfaceToken(IMemory::getInterfaceDescriptor());
if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) {
sp<IBinder> heap = reply.readStrongBinder();
ssize_t o = reply.readInt32();
size_t s = reply.readInt32();
if (heap != 0) {
mHeap = interface_cast<IMemoryHeap>(heap);
if (mHeap != 0) {
size_t heapSize = mHeap->getSize();
if (s <= heapSize
&& o >= 0
&& (static_cast<size_t>(o) <= heapSize - s)) {
mOffset = o;
mSize = s;
} else {
// Hm.
android_errorWriteWithInfoLog(0x534e4554,
"26877992", -1, NULL, 0);
mOffset = 0;
mSize = 0;
}
}
}
}
}
if (offset) *offset = mOffset;
if (size) *size = mSize;
return (mSize > 0) ? mHeap : 0;
}

通过 Binder 机制,从服务端读取偏移量,大小,并返回 BpMemory 代理实例(mHeap)。

Ashmem 总结

  • 文件描述符
    在形式上是一个非负整数,实际上它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。
  • 进程间共享原理
    Ashmem 进程间共享原理:两个在不同进程中的文件描述符对应同一个指向设备文件 /dev/ashmem 的文件结构体。
    Binder 机制在数据交互时大小不能超过 1M,可以通过传递匿名共享内存(Ashmem)的文件描述符或者 IBinder 对象(IMemoryHeap/IMemory),来实现大数据的共享。
  • 优缺点
    匿名共享内存不会占用 Heap,不会导致 OOM,但是如果肆意使用,会导致系统资源不足性能下降。
  • 应用场景
    camera.preview 预览,SurfaceFlinger 绘制等,可以看到匿名共享内存的应用。

参考文档

0%