首页 > 系统相关 >MemoryFile 共享内存原理分析

MemoryFile 共享内存原理分析

时间:2023-08-15 13:04:13浏览次数:34  
标签:MemoryFile int mmap fd ashmem 原理 共享内存 Ashmem

MemoryFile 共享内存原理分析_Memory

Android 上层提供了一些内存共享工具类,比如 MemoryFile。你使用过吗?知道它的实现原理吗?

MemoryFile 是 Java 层对 Ashmem 的一个封装,下面来一起学习 MemoryFile,掌握它的使用姿势和底层原理。

MemoryFile 使用方法大致如下:

「进程 A 中申请一块共享内存写入数据,并准备好文件描述符:」

MemoryFile memoryFile = new MemoryFile(name, size);
memoryFile.getOutputStream().write(data);
Method method = MemoryFile.class.getDeclaredMethod("getFileDescriptor");
FileDescriptor des = (FileDescriptor) method.invoke(memoryFile);
ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(des);

「进程 B 中通过 binder 拿到 A 进程中准备好的文件描述符,然后直接读取数据:」

FileDescriptor descriptor = pfd.getFileDescriptor();
FileInputStream fileInputStream = new FileInputStream(descriptor);
fileInputStream.read(data);

如上代码所示,使用起来和文件读写一样很简单,但我们不能只停留在仅会使用的浅显层面。接着来看 MemoryFile 是怎么从 Java API 调用到 Ashmem 驱动函数的,先来看 MemoryFile 的构造函数:

public MemoryFile(String name, int length) throws IOException {    
try { 
mSharedMemory = SharedMemory.create(name, length);        
mMapping = mSharedMemory.mapReadWrite();    
} catch (ErrnoException ex) {        
ex.rethrowAsIOException();   
 }
}

可以看到构造 MemoryFile 时通过 SharedMemory create 方法申请了一块匿名共享内存,SharedMemory create 方法中调用了 nCreate native 方法:

private static native FileDescriptor nCreate(String name, int size) throws ErrnoException;

对应的 native 实现在 android_os_SharedMemory.cpp 中,实现如下:

static jobject SharedMemory_create(JNIEnv* env, jobject, jstring jname, jint size) {    
const char* name = jname ? envGetStringUTFChars(jname, nullptr) : nullptr;   
int fd = ashmem_create_region(name, size); //创建匿名共享内存    
...   
 return jniCreateFileDescriptor(env, fd);
}

接着来看 ashmem_create_region 方法,它的对应实现在 ashmem-dev.cpp 中,如下:

int ashmem_create_region(const char *name, size_t size){  
  int ret, save_errno;    //创建匿名共享内存
int fd = __ashmem_open();    
 if (fd < 0) { return fd;}    
if (name) {       
 char buf[ASHMEM_NAME_LEN] = {0};     
   strlcpy(buf, name, sizeof(buf));     
   // 设置 Ashmem 名字
ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_NAME, buf));  
       if (ret < 0) {goto error; }   
 }
}

如上设置 Ashmem 名字执行了 ioctl 系统调用, 它会进一步调用到 「ashmem_ioctl」 驱动函数。接着来看 __ashmem_open 方法:

static int __ashmem_open(){
int fd;   
 pthread_mutex_lock(&__ashmem_lock);    
fd = __ashmem_open_locked(); //创建匿名共享内存
 pthread_mutex_unlock(&__ashmem_lock);    
return fd;
}

进一步调用到 __ashmem_open_locked 方法,如下:

#define ASHMEM_DEVICE "/dev/ashmem" //Ashmem 设备驱动
static int __ashmem_open_locked(){
int ret;    
struct stat st;   
 int fd = TEMP_FAILURE_RETRY(          
      open(ASHMEM_DEVICE, O_RDWR | O_CLOEXEC)); //创建匿名共享内存  
  ...    return fd;
}

在 __ashmem_open_locked 方法中调用了 open() 系统调用,和 ioctl 一样,对应到 Ashmem 设备驱动函数了,即 「ashmem_open」 驱动函数。

通过上面的分析知道 Ashmem 驱动的 ashmem_open 函数是由 SharedMemory 的 create 方法触发一步一步调用到的,期间还调用了 ashmem_ioctl 函数。

而 ashmem_mmap 驱动函数是通过 SharedMemory 的 mapReadWrite 方法触发,下面来分析这个过程:

//android.os.SharedMemory.java
public @NonNull ByteBuffer mapReadWrite() throws ErrnoException {    
return map(OsConstants.PROT_READ | OsConstants.PROT_WRITE, 0, mSize);
}

public @NonNull ByteBuffer map(int prot, int offset, int length) throws ErrnoException {    
...   
 long address = Os.mmap(0, length, prot, OsConstants.MAP_SHARED, mFileDescriptor, offset);    
...    return new DirectByteBuffer(length, address, mFileDescriptor, unmapper, readOnly);}

比较关键的是 mFileDescriptor,它是执行 SharedMemory create 方法申请匿名共享内存后,返回的文件描述符。继续来跟踪 mmap 调用:

//android.system.Os.java
public static long mmap(long address, long byteCount, int prot, int flags, FileDescriptor fd, long offset) throws ErrnoException {    
return Libcore.os.mmap(address, byteCount, prot, flags, fd, offset);
}

//libcore.io.Libcore.java
public final class Libcore {    
private Libcore() { }    
public static Os rawOs = new Linux();  
  public static Os os = new BlockGuardOs(rawOs);
}

//libcore.io.Linux.java
public native long mmap(long address, long byteCount, int prot, int flags, FileDescriptor fd, long offset) throws ErrnoException;

Libcore 中使用 BlockGuardOs 对 Linux 进行了一层包装,但实际还是通过 Linux 来执行的,最后调用到 Linux 中的 native mmap 方法,native 中对应的实现是 mmap.cpp:

void* mmap(void* addr, size_t size, int prot, int flags, int fd, off_t offset) {   
 return mmap64(addr, size, prot, flags, fd, static_cast<off64_t>(offset));
}

到此为止,由 SharedMemory 的 mapReadWrite 方法调用到 native mmap 函数,传递的关键参数是文件描述符,后续它将这样调用到 ashmem_mmap:

  1. 通过 fd 可以找到所属设备,也就是 Ashmem 设备
  2. 调用 Ashmem 设备的 ashmem_mmap 驱动函数

关键代码如下:

if(file){  
  ...
    error = file->f_op->mmap(file,vma);   
 ...
}

file 代表文件或设备驱动,这里指的就是 Ashmem 设备,f_op 就是 Ashmem 设备驱动函数集,也就是注册的 Ashmem 设备描述,至此便是 ashmem_mmap 驱动函数的调用过程。

想要了解更多Anrloid相关知识可以点击下方课堂链接                  https://edu.51cto.com/course/32703.html Android课

标签:MemoryFile,int,mmap,fd,ashmem,原理,共享内存,Ashmem
From: https://blog.51cto.com/u_16163452/7086913

相关文章

  • socket编程原理
    socket编程原理1问题的引入UNIX系统的I/O命令集,是从Maltics和早期系统中的命令演变出来的,其模式为打开一读/写一关闭(open-write-read-close)。在一个用户进程进行I/O操作时,它首先调用“打开”获得对指定文件或设备的使用权,并返回称为文件描述符的整型数,......
  • CGI的基本原理
    一.基本原理CGI:通用网关接口(CommonGatewayInterface)是一个Web服务器主机提供信息服务的标准接口。通过CGI接口,Web服务器就能够获取客户端提交的信息,转交给服务器端的CGI程序进行处理,最后返回结果给客户端。组成CGI通信系统的是两部分:一部分是html页面,就是在用户端浏览器上显示的页......
  • 字节码角度看synchronized和反射的实现原理
    前几天,关于字节码技术,我们讲了字节码的基础,常见的字节码框架以及在软件破解和APM链路监控方面的一些应用.今天我们回到Java本身,看下我们常用的synchronized关键字和反射在字节码层面是如何实现的.synchronized代码块级别的synchronized如下方法的内部使用了synchronized......
  • 栈(Stack)的基本原理及算法实现
    栈(Stack)的基本原理及算法实现一、栈的基本概念栈(Stack)是一种后进先出(LIFO,LastInFirstOut)的线性表,其特点是只允许在一端进行插入操作,而在另一端进行删除操作。栈的基本操作有:入栈(push)、出栈(pop)、查看栈顶元素(top)等。二、栈的实现原理数组实现:使用一组连续的内存空间来存储......
  • Java 日志系列:JUL 使用和原理分析
    目录一、简介二、使用三、日志级别四、Logger继承关系五、配置文件六、原理解析一、简介JUL全称JavautilLogging是java原生的日志框架,使用时不需要另外引用第三方类库,相对其他日志框架使用方便,学习简单,能够在小型应用中灵活使用。Loggers:被称为记录器,应用程序通过获......
  • Step-by-step to LSTM: 解析LSTM神经网络设计原理
    Ps:喂喂喂,你萌不要光收藏不点赞呀_(:з」∠)_emmmm...搞清楚LSTM中的每个公式的每个细节为什么是这样子设计吗?想知道simpleRNN是如何一步步的走向了LSTM吗?觉得LSTM的工作机制看不透?恭喜你打开了正确的文章!零、前置知识1:在上一篇文章《前馈到反馈:解析RNN》中,小夕从最简单的无......
  • synchronized的作用?synchronized的原理?
    引言​在并发编程中,为了保证线程安全和数据一致性,Java提供了synchronized关键字来实现对共享资源的同步访问。synchronized关键字可以应用于方法和代码块,它在多线程环境下起到了重要的作用。本文将深入探讨synchronized的作用和原理,并给出相应的代码示例。synchronized的作用sy......
  • 图解 history api 和 React Router 实现原理
    Router是开发React应用的必备功能,那ReactRouter是怎么实现的呢?今天我们就来读一下ReactRouter的源码吧!首先,我们来学一下HistoryAPI,这是基础。什么是history呢?就是这个东西:我打开了一个新的标签页、然后访问baidu.com、sougou.com、taobao.com。长按后退按钮,就会列出......
  • cookie和session的区别及原理
    Cookie概念在浏览某些网站时,这些网站会把一些数据存在客户端,用于使用网站等跟踪用户,实现用户自定义功能.是否设置过期时间:如果不设置过期时间,则表示这个Cookie生命周期为浏览器会话期间,只要关闭浏览器,cookie就消失了.这个生命期为浏览会话期的cookie,就是会话C......
  • Nginx 基本原理与最小配置
    文章和代码已经归档至【Github仓库:<https://github.com/timerring/front-end-tutorial>】或者公众号【AIShareLab】回复nginx也可获取。目录结构进入Nginx的主目录有如下文件夹client_body_tempconffastcgi_temphtmllogsproxy_tempsbinscgi_tempuwsgi_temp其中以_temp结......