内存共享和大块内存的使用,在实际场景下面的需求是很多的,这里,举三个简单的应用场景:
- 用户态和内核态共享内存
- 用户态不同进程内存共享
- 内核态中使用ION分配buffer
用户态和内核态共享内存
在Android的BSP代码中有一个ion的library封装了一些对ion驱动设备操作的接口system/core/libion/
1 | int ion_open(); |
可以参考改目录下的ion.c
实现代码,都是对ion驱动设备的open/close/ioctl等操作。
初始化ion获取文件描述符,分配内存,获取内存共享的文件描述符
1 | void init_ion() |
ion_open()
函数打开/dev/ion
设备获取文件描述符,后面对文件的操作都是基于这个文件描述符ion_alloc()
指定分配的buffer大小和类型,需要注意的是buffer的size,在camera等一些应用场景下经常会被要求buffer要4K对齐,返回的是ion_handler
ion_share()
函数把指定ion_handler
内存共享出去,获取到内存共享文件描述符,把shared_fd
发送到另外一个进程中,然后通过mmap
函数进行内存映射。
通过mmap
函数把ion_handle
转化为虚拟地址
1 | g_buf = (char *)mmap(NULL, HON_ZONE_SIZE, |
本例中需要把buffer传到内核空间去,然后在kernel空间对buffer进行读写,首先需要把shared_fd
传到kernel空间,然后通过shared_fd
获取到ion_handler
最后得到内核虚拟机地址。
使用ioctl
把shared_fd传到内核空间
1 | void bind_driver() |
本文中自己实现了一个字符驱动hon_zone
用来配合用户空间的进程读写用户空间分配的ion buffer,用户态使用ioctl把shared_fd传下去,来看kernel是如何处理的:
1 | case HON_ZONE_ION_INIT: |
copy_from_user
拿到用户态传下来的shared_fd
- 创建
ion_client
这里使用的是高通的内核,封装的函数msm_ion_client_create
获取ion_client
ion_import_dma_buf
把shared_fd
转化为ion_handle
- 通过
ion_map_kernel
传入ion_handle
获取到虚拟地址,与用户态的mmap
函数功能类似
用户态不同进程共享内存
写一个简单的例子介绍如何在不同进程中进行内存共享
创建socketpair
,获取两个可以连通的socket文件描述符,这两个文件描述符分别给父子进程使用,一个写一个读,这里其实和使用管道是类似的作用,但是管道是单向的,socketpair
是双向读写的。
1 | int pipefd[2]; |
fork
子进程,在子进程中获取ION buffer的共享内存文件描述符
1 | int pid = fork(); |
获取shared_fd然后通过socket发送给父进程
1 | int getIONBuffer() |
- 打开
/dev/ion
获取client文件描述符 - 调用
ioctl
来分配ION buffer获取ion handle - 利用
mmap
映射虚拟地址,最终操作的是虚拟地址数据 - 返回shared_fd
1 | void send_fd(int fd, int fd_to_send) |
通过socket
进程间通信把shared_fd发送给父进程
父进程中获取共享buffer使用
1 | int pagesize = 4096; |
- 通过
recv_fd
函数获取到子进程发送过来的shared_fd - 调用
ioctl
传入shared_fd获取ion handle - 调用
mmap
映射成可以操作的虚拟地址 - 对数据进行读写操作
可以看到在父子进程中都对/dev/ion
设备打开,而且在不同的进程中制允许有一个ion_client实例。
1 | int recv_fd(int fd) |
总结
总的来说,在Android中使用ION机制进行共享内存还是很方便的。其中比较有意思的是如何把shared_fd
传到不同的进程中,本例使用的是socketpair
机制,在Android 8或以上的版本中,Android官方推荐使用binder机制通过把shared_fd封装成native_handler
发送给别的进程。
有兴趣的可以去尝试一下。