Ashmem
Android 系统的匿名共享内存 Ashmem 驱动程序利用了 Linux 的共享内存子系统导出的接口来实现.
在 Android 系统中, 匿名共享内存也是进程间通信方式的一种.
相比于 malloc 和 anonymous/named mmap 等传统的内存分配机制, Ashmem 的优势是通过内核驱动提供了辅助内核的内存回收算法机制 (pin/unpin).
内存回收算法机制就是当你使用 Ashmem 分配了一块内存, 但是其中某些部分却不会被使用时, 那么就可以将这块内存 unpin 掉.
unpin 后, 内核可以将它对应的物理页面回收, 以作他用. 你也不用担心进程无法对 unpin 掉的内存进行再次访问, 因为回收后的内存还可以再次被获得 (通过缺页 handler), 因为 unpin 操作并不会改变已经 mmap 的地址空间.
Android 匿名共享内存接口
源码是最好的老师, 废话不多说, 直接看代码.
源码路径: system/core/libcutils/ashmem-dev.c
Android 源码中, ashmem 的实现:
打开共享内存:
- /*
- * ashmem_create_region - creates a new ashmem region and returns the file
- * descriptor, or <0 on error
- *
- * `name' is an optional label to give the region (visible in /proc/pid/maps)
- * `size' is the size of the region, in page-aligned bytes
- */
- 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));
- ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_NAME, buf));
- if (ret < 0) {
- goto error;
- }
- }
- ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_SIZE, size));
- if (ret < 0) {
- goto error;
- }
- return fd;
- error:
- save_errno = errno;
- close(fd);
- errno = save_errno;
- return ret;
- }
在函数中调用驱动接口:
__ashmem_open
__ashmem_open 函数的实现如下:
- /* logistics of getting file descriptor for ashmem */
- static int __ashmem_open_locked()
- {
- int ret;
- struct stat st;
- int fd = TEMP_FAILURE_RETRY(open(ASHMEM_DEVICE, O_RDWR));
- if (fd < 0) {
- return fd;
- }
- ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
- if (ret < 0) {
- int save_errno = errno;
- close(fd);
- errno = save_errno;
- return ret;
- }
- if (!S_ISCHR(st.st_mode) || !st.st_rdev) {
- close(fd);
- errno = ENOTTY;
- return -1;
- }
- __ashmem_rdev = st.st_rdev;
- return fd;
- }
- static int __ashmem_open()
- {
- int fd;
- pthread_mutex_lock(&__ashmem_lock);
- fd = __ashmem_open_locked();
- pthread_mutex_unlock(&__ashmem_lock);
- return fd;
- }
可见函数最后是通过 open 去操作 ashmem 驱动文件.
返回为一个文件描述符.
- int ashmem_valid(int fd)
- {
- return __ashmem_is_ashmem(fd, 0)>= 0;
- }
除此之外, 源码中还提供了几个接口函数:
1. 锁定匿名共享内存块
- int ashmem_pin_region(int fd, size_t offset, size_t len)
- {
- struct ashmem_pin pin = { offset, len };
- int ret = __ashmem_is_ashmem(fd, 1);
- if (ret < 0) {
- return ret;
- }
- return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_PIN, &pin));
- }
2. 解锁匿名共享内存块
- int ashmem_unpin_region(int fd, size_t offset, size_t len)
- {
- struct ashmem_pin pin = { offset, len };
- int ret = __ashmem_is_ashmem(fd, 1);
- if (ret < 0) {
- return ret;
- }
- return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_UNPIN, &pin));
- }
3. 获取大小
- int ashmem_get_size_region(int fd)
- {
- int ret = __ashmem_is_ashmem(fd, 1);
- if (ret < 0) {
- return ret;
- }
- return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL));
- }
因为是文件描述符, 所以关闭直接采用 close.
close(fd)
使用例子
创建共享内存
- fd = ashmem_create_region(NULL,length);
- if(fd < 0)
- printf("Creating code cache, ashmem_create_region error.");
将共享内存映射到用户空间
- data = (char *)mmap(NULL, data.length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
- if(data != MAP_FAILED){
- printf("mmap sharemem success....");
- memcpy(data.data,gucDotBuffer,length);
- }else{
- printf("mmap sharemem failed....'%s'",strerror(errno));
- }
关闭映射并关闭共享内存文件
- munmap(data,length);
- close(fd);
来源: http://www.jianshu.com/p/41c4b1bf4873