Anonymous Shared Memory android 匿名共享内存

原址
两个特点:

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

Android系统的匿名共享内存子系统的主体是以驱动程序的形式实现在内核空间的。

应用程序框架层的Java调用接口是通过JNI方法来调用库层的C/C++调用接口
android应用程序框架层提供了一个MemoryFile接口来封装了匿名共享内存文件的创建和使用
它实现在frameworks/base/core/java/android/os/MemoryFIle.java文件中

MemoryFile.java 的构造函数:(两种创建匿名共享内存的方法)

public class MemoryFile {
    ...
    public MemoryFile(String name, int length) throws IOException {
        mLength = length;
        mFD = native_open(name, length);
        mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);
        mOwnsRegion = true;
    }
    ...
    public MemoryFile(FileDescriptor fd, int length, String mode) throws IOException {
        if (fd == null) {
            throw new NullPointerException("File descriptor is null")
        }
        if (!isMemoryFile(fd)) {
            throw new ILlegalArgumentException("Not a memory file.");
        }
        mLength = length;
        mFD = fd;
        mAddress = native_mmap(mFD, length, modeToProt(mode));
        mOwnsRegion = false;
    }
    ...
}

第一种方法是以指定的字符串调用jni方法native_open()来创建一个匿名共享内存文件,得到mFD文件描述符, 把其传入进native_mmap()映射在进程空间中,便可以通过这个映射得到的地址空间来直接访问内存数据。
第二种则是以指定的文件描述符来直接调用JNI方法native_mmap(...)把这个匿名共享内存文件映射在进程空间中,然后进行访问。要判断该fd是否为匿名共享内存文件的文件描述符isMemoryFile(fd)

具有辅助内存管理系统来有效的管理内存的特点

Ashmem 驱动程序的源代码
ashmem机制是建立在Linux内核实现的共享内存的基础上的,同时向Linux内存管理系统的内存回收算法注册接口,告诉linux内存管理系统它的某些内存块不再使用了,可以被回收了。

MemoryFile接口

创建(open), 映射(mmap), 读写(read/write)以及锁定和解锁(pin/unpin)四个使用场景
先看Ashmem驱动程序模块的初始化函数
ashmem驱动程序实现在kernel/common/mm/ashmem.c文件中,初始化函数为ashmem_init

static struct file_operations ashmem_fops = {
    .owner = THIS_MODULE,
    .open = ashmem_open,
    .release = ashmem_release,
    .mmap = ashmem_mmap,
    .unlocked_ioctl = ashmem_ioctl,
    .compat_ioctl = ashmem_ioctl,
};

static struct miscdevice ashmem_misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "ashmem",
    .fops = &ashmem_fops,
};

static int __init ashmem_init(void)  {
    int ret;
    ......

    ret = misc_register(&ashmem_misc);
    if (unlikely(ret)) {
        printk(KERN_ERR "ashmem: failed to register misc device!\n");
        return ret;
    }
    ......

    return 0;
}

匿名共享内存的创建过程(open)
MemoryFile第一个构造函数中通过JNI的native_open来创建,
而这个方法的实现在frameworks/base/core/jni/adroid_os_MemoryFile.cpp,
在里面又会调用ashmem_create_region()来创建匿名共享内存,这个函数实现在
system/core/libcutils/ashmem-dev.c文件中,在region()中会先open,
然后两次ioctl, open()是打开设备文件ASHMEM_DEVICE, 后面的分别是设置匿名共享内存的名称和大小。

struct ashmem_area 它是Ashmem驱动程序的一个相关数据结构,用于表示一块共享内存。
它的实例都是从自定义的一个slab缓冲区(是在初始化函数创建的)创建的

在region函数里面(open函数):

//执行打开文件的操作
fd = open(ASHMEM_DIVICE, O_RDWR);
//ASHMEM_DIVICE是个宏,定义为: #define ASHMEM_DIVICE "/dev/ashmem"(共享内存设备文件)

iconl函数:

//调用两次iconl,匿名共享内存的名称和大小
//在kernel/common/mm/include/ashmem.h文件中,
#define ASHMEM_NAME_LEN 256
#define __ASHMEMIOC 0x77
#define ASHMEM_SET_NAME _IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEK])
#define ASHMEM_SET_SIZE _IOW(__ASHMEMIOC, 3, size_t)

ASHMEM_SET_NAME 命令的iconl调用,最终进入到Ashmem驱动程序的ashmem_iconl函数中, 依次调用set_name(),将名字映射到asma->name中;
同理ASHMEME_SET_SIZE,将大小保存在对应的asma->size域中

ashmem的创建完成

ashmem 的内存映射操作(mmap)

MemoryFile类的构造函数中, 进行了匿名共享内存的创建操作后,然后就是要把匿名共享内存设备文件映射到空间中:

public class MemoryFile {
    private static native int native_mmap(FileDescriptor fd, int length, int mode) throws IOException;
    ...
}

映射是通过JNI方法native_mmap来进行的,它的实现是在android_os_MemoryFile.cpp:

static jint android_os_MemoryFile_mmap(JNIEnv * env, jobject clazz, jobject, jobject fileDescriptor jint length, jint prot) {
    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
    jint result = (jint)mmap(NULL, length, prot, MAP_SHARED, fd, 0);
    if (!result) {
        jniThrowException(env, "java/io/IOException", "mmap failed");
    }
    return result;
}

fd是在签名open匿名设备文件/dev/ashmem获得的, 有这个文件描述符后,就可以直接通过mmap来执行内存映射操作了。最后调用进入到Ashmem驱动程序的ashmem_mmap函数中:

static int ashmem_mmap(struct file *file, struct vm_area_struct *vma) {
    struct ashmem_area *asma = file -> private_data;
    int ret = 0;
    mutex_lock(&ashmem_mutex);

    //
    if (unlikely(!asma->size)) {
        rt = -EINVAL;
        goto out;
    }
    //
    if (unlikely((vma->vm_falags & ~asma->prot_mask) & PROT_MASK)) {
        ret = -EPERM;
        goto out;
    }
    /*requested protection bits must match our allowed protection mask*/
    if (!asma-> file) {
        char *name = ASHMEM_NAME_DEF;
        struct file *vmfile;
        //
        if (asma->name[ASHMEM_NAME_PREFIX_LEN] != '\0')
            name = asma -> name;
        /* 主要函数 ...and allocate the backing shmem file */
        vmfile = shmem_file_setup(name, asma->size, vma->vm_flags);
        if (unlikely(IS_EFR(vmfile))) {
            ret = PTR_ERR(vmfile);
            goto out;
        }
        asma->file = vmfile;
    }
    get_file(asma->file);
    if (vma->vm_flags & VM_SHARED)
        shmem_set_file(vma, asma->file);
    else {
        if (vma->vm_file)
            fput(vma->vm_file)
        vma->vm_file = asma->file;
    }
    vma->vm_flags |= VM_CAN_NONLINEAR;
out:
    mutex_unlock(&ashmem_mutex);
    return ret;
}

调用了Linux 内核提供的shmem_file_setup 函数来在临时文件系统tmpfs中创建一个临时文件a,
a与Ashmem驱动程序创建的匿名共享内存对应。

Linux 内核中的共享内存机制其实是一种进程间通信机制(IPC)

通过shmem_file_setup函数创建的临时文件vmfile最终保存在vma->file中。
vma是struct vm_area_struct类型,表示的是当前进程空间中一块连续的虚拟地址空间

同时这个临时文件vmfile也会保存在asma->file域中, 这样Ashmem驱动程序就可以通过
asma->file来操作这个匿名共享文件了。

函数ashmem_mmap执行完成后,经过层层返回到JNI方法native_mmap中去,后面,共享内存的读写操作就是对这个地址空间进行操作了。

匿名共享内存的读写操作

MemoryFile的匿名共享内存读写操作都是通过JNI方法来实现的,读操作和写操作的JNI方法分别是: native_readnative_write.

因为前面的mmap得到了文件的地址,所以可直接进行访问,不必进入到Ashmem驱动程序中去。
这也是Ashmem没有提供readwrite文件操作的原因.

其中利用到了ashmem_pin_regionashmem_unpin_region两个函数是系统运行时库提供的接口,用来执行匿名共享内存的锁定和解锁操作。

匿名共享内存的锁定和解锁

它们的作用是告诉Ashmem驱动程序,它的哪些内存块是正在使用的,需要锁定,哪些内存是不需要使用了,可以让它解锁。
这样,Ashmem驱动程序就可以辅助内存管理系统来有效地管理内存了
实现在system/core/libcutils/ashmem/ashmem-dev.c文件中:

int ashmem_pin_region(int fd, size_t offset, size_t len) {
    struct ashmem_pin pin = { offser, len };
    return ioctl(fd, ASHMEM_PIN, &pin);
}
//
int ashmem_unpin_region(int fd, size_t offset, size_t len) {
    struct ashmem_pin pin = { offser, len };
    return ioctl(fd, ASHMEM_UNPIN, &pin);
}

它们的实现很简单,由ASHMEM_PIN和ASHMEM_UNPIN 两个ioctl 操作来实现匿名共享内存的锁定和解锁操作。

它们定义在kernel/common/include/linux/ashmem.h文件中

#define __ASHMEMIOC  0x77

#define ASHMEM_PIN    _IOW(__ASHMEMIOC, 7, struct ashmem_pin)
#define ASHMEM)UNPIN    _IOW(__ASHMEMIOC, 8, struct ashmem_pin)

参数类型为struct ashmem_pin, 定义在这个文件下:

struct ashmem_pin {
    __u32 offset;
    __u32 len;
};

这个结构体中只有两个域, 分别表示要锁定或者要解锁的内存块的起始大小以及大小。

搜索unpinnd

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值