Ashmem 简述

Ashmem是Android的匿名共享内存机制,基于mmap实现进程间内存共享。它在内核中以驱动形式存在,通过文件描述符传递句柄。Ashmem添加互斥锁实现同步,并协助内存管理。其工作流程包括内存映射、设备注册、内存分配等。在Java层,通过MemoryFile和SharedMemory类操作。应用中,通过Binder连接传递fd进行数据传输。Ashmem帮助优化跨进程通信效率。
摘要由CSDN通过智能技术生成

1. 概述

Ashmem(Anonymous Shared Memory,Android 匿名共享内存),它基于 mmap 系统调用,可以让不同进程将同一段物理内存映射到各自的虚拟地址中,从而实现内存共享。

它以驱动程序的形式在内核空间中实现,并通过文件描述符来传递共享内存的句柄。

相对于 Linux 的共享内存, Ashmem 驱动中添加了互斥锁以此实现了同步的机制,并能够辅助内存管理系统来有效地管理不再使用的内存块。

2. 原理

两个进程就像两个平行的世界,A 进程没法直接访问 B 进程的数据,其用户空间的数据存在进程隔离,而内核空间的数据则可以进程间共享。

进程隔离
32 位系统内存空间

应用程序不能直接操作设备地址,操作系统通过内存映射(mmap),可以把设备地址映射到进程内核空间的虚拟内存区域。
内存映射

Android 中,/dev/ashmem是一个虚拟设备,不存在实际文件,只在内核驱动中对应一个inode节点。通过 ashmem 和 binder 相关方法,存在于两个进程中的不同文件描述符,会对应到同一个基于/dev/ashmem创建的临时文件,并将该文件指向的物理内存分别映射到各个进程自己的虚拟内存中,最终实现进程间内存共享。
ashmem文件

3. 源码

3.1 源码路径

Framework:
  frameworks/base/core/java/android/os/SharedMemory.java
  frameworks/base/core/java/android/os/MemoryFile.java
  frameworks/base/core/jni/android_os_SharedMemory.cpp
  frameworks/base/core/jni/android_os_MemoryFile.cpp

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

System:
  system/core/libcutils/ashmem-host.c   提供给模拟器
  system/core/libcutils/ashmem-dev.c    提供给实际设备
  system/core/include/cutils/ashmem.h

Driver:  
  kernel/msm-5.4/drivers/staging/android/ashmem.c
  kernel/msm-5.4/drivers/staging/android/ashmem.h
  kernel/msm-5.4/drivers/staging/android/uapi/ashmem.h

3.2 驱动层

依托于/dev/ashmem设备,Linux 内核分配并管理 ashmem 的全局内存。

kernel/msm-5.4/drivers/staging/android/ashmem.c
kernel/msm-5.4/drivers/staging/android/ashmem.h

ashmem_area 结构体

/**
 * 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 prot_mask;         // allowed prot bits, as vm_flags
};

ashmem_init()

  1. ashmem 的 init 入口,通过 kmem_cache_create(),创建ashmem_area_cachepashmem_range_cachep两个 kmem_cache 对象,此时并没有分配内存;
  2. 通过调用misc_register()将其注册为ashmem名称的 misc 设备;
  3. 通过调用register_shrinker()向内存管理系统注册内存回收函数。

ashmem_open()

  1. 从 ashmem_area_cachep 中分配一块 ashmem_area(asma)共享内存给进程;
  2. 对 asma 进行初始化,然后将其记录在 file->private_data 中,进程可以通过返回的 file 指针访问该共享内存。

ashmem_mmap()

Memory Map(内存映射),把 /dev/ashmem 设备文件映射到进程虚拟内存中。

  1. file->private_data中取出 ashmem_open() 时创建的 asma;
  2. asma->file为空,则当前为第一次访问该共享空间的进程,需要调用 Linux 提供的 shmem_file_setup() ,在 tmpfs 中创建临时文件用于进程间的内存共享。
    asma 文件指针

ashmem_ioctl()

  • 设置/获取名称;
  • 设置/获取 size 大小;
  • 设置/获取保护位;
  • 锁定/解锁 asma 下的内存块 。

ashmem_pin_unpin()

锁定(pin)或解锁(unpin)内存块。

Ashmem 机制中,正在使用的内存块需要被锁定,不被使用的内存块需要被解除锁定。unpin操作仅改变相关状态标记,并不会改变已经 mmap 的地址空间。因此,用户可以在解锁后重新锁定某块内存块。

Ashmem 机制建立在 Linux 内核的共享内存实现上。同时又向 Linux 内存管理系统的内存回收算法注册接口。系统内存不足时,会依据 LRU 算法回收unpin内存块对应的物理页面。如果不希望内存对象被回收,可以通过修改其状态为pin来保护它。
asma 内存块

3.3 系统层

system/core/libcutils/ashmem-dev.c
system/core/include/cutils/ashmem.h

Ashmem 匿名共享内存,需要依赖 System 层和 Driver 层进行交互:

// 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);

3.4 Native层

frameworks/native/libs/binder/MemoryDealer.cpp

MemoryDealer类可以看做是对 ashmem 的封装,源码位于frameworks/native/libs/binder/目录下。其内部拥有两个重要的成员变量:

  • mHeap,为MemoryHeapBase类对象,用于描述一块内存;
  • mAllocator,为SimpleBestFitAllocator类对象,用于分配内存。

MemoryHeapBase继承自抽象类BnMemoryHeap,并使用libcutils库中的 ashmem_create_regionmmap方法进行 ashmem 创建和内存映射。而MemoryBase继承自BnMemory,用于包装MemoryHeapBase对象。

IMemory.h 中,定义了IMemoryIMemoryHeapBnMemoryBnMemoryHeap等匿名共享内存相关的访问接口。几者间的关系如下:
Native层接口关系

3.5 Framework 层

frameworks/base/core/java/android/os/SharedMemory.java
frameworks/base/core/java/android/os/MemoryFile.java

Java层借助MemoryFile或者SharedMemory创建匿名共享内存。

SharedMemory通过 JNI 调用ashmem_create_region进行匿名共享内存区创建。

MemoryFileSharedMemory进行了包装,能够标记不再使用的内存区域,方便系统回收。

4. Ashmem 的应用

ashmem 通过共享内存进行数据传递,共享内存的操作需要通过文件句柄 fd。所以,当不同进程间进行内存共享时,需要跨进程传递 fd 信息。因此,在使用 Ashmem 之前,需要在两个进程之前建立 Binder 连接。

通过 Ashmem 传递内容需要进行以下步骤:

  1. 创建 Ashmem 区域,并写入待传输数据;
  2. 获得该共享内存的 fd 句柄;
  3. 打包 fd 到 bundle;
  4. 通过 Binder 发送 fd 信息到服务端。

5. ashmem 设备查看

在使用时,进程在 /dev/ashmem/ 目录中创建了一个文件条目,然后删除,但因为它至少有一个打开的文件描述符,所以相应的inode和对应内存区域仍然存在。创建多个具有相同名称的 ashmem 区域,它们都显示为/dev/ashmem/<name> (deleted),但它们中的每一个都对应于不同的inode,因此对应着不同的内存区域。当最后一个文件描述符关闭时,ashmem 临时文件的inode和对应内存会自动回收。
查看 ashmem 设备文件

5. 总结

总结


参考链接:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值