Android系统的Ashmem匿名共享内存子系统分析(3)- Ashmem子系统的 C/C++访问接口

news2024/12/23 19:10:49

声明

  • 其实对于Android系统的Ashmem匿名共享内存系统早就有分析的想法,记得2019年6、7月份Mr.Deng离职期间约定一起对其进行研究的,但因为我个人问题没能实施这个计划,留下些许遗憾…
  • 文中参考了很多书籍及博客内容,可能涉及的比较多先不具体列出来了;
  • 本文使用的代码是LineageOS的cm-14.1,对应Android 7.1.2,可以参考我的另一篇博客:cm-14.1 Android系统启动过程分析(1)-如何下载Nexus5的LineageOS14.1(cm-14.1)系统源码并编译、刷机
  • 若对 Binder 不熟悉,可参考专栏:Android系统中的Binder通信机制分析

1 Ashmem的架构

  Android 系统实现的 Ashmem 匿名共享内存子系统,用来在应用程序之间共享数据。Ashmem 与传统的Linux系统实现的共享内存一样,都是基于内核提供的临时文件系统tmpfs实现的,但是 Ashmem 对内存块进行了更为精细化的管理。应用程序可以动态地将一块匿名共享内存划分为若干个小块,当这些小块内存不再需要使用时,它们就可以被内存管理系统回收。通过这种动态的、分而治之的内存管理方式,Android系统就能够有效地使用系统内存,适应内存较小的移动设备环境。

  匿名共享内存系统是以Ashmem驱动程序为基础的,系统中所有的匿名共享内存都由Ashmem驱动程序负责分配和管理。Android系统在 Native 层提供了 C/C++ 调用接口和 Framework 层提供了 Java 调用接口。

  • 在Framework 层中,提供了两个C++类 MemoryBase 和 MemoryHeapBase,以及一个 Java 类 MemoryFile 来使用匿名共享内存。
  • 在运行时库 cutils 中,主要提供了三个C函数 ashmem_create_region、ashmem_pin_region 和 ashmem_unpin_region 来访问 Ashmem 驱动程序。

  Ashmem驱动程序在启动时,会创建一个 /dev/ashmem 设备文件,这样,运行时库 cutils 中的匿名共享内存接口就可以通过文件操作函数 open 和 ioctl 等来访问 Ashmem 驱动程序。
在这里插入图片描述

  传统的 Linux 系统使用一个整数来标志一块共享内存,而 Android 系统则使用一个文件描述符来标志一块匿名共享内存。使用文件描述符来描述一块匿名共享内存有两个好处:

  1. 可以方便地将它映射到进程的地址空间,从而可以直接访问它的内容;
  2. 可以使用 Binder 进程间通信机制来传输这个文件描述符,从而实现在不同的应用程序之间共享一块匿名内存。

  Binder 进程间通信机制使用一个类型为 BINDER_TYPE_FD 的 Binder 对象来描述一个文件描述符,当 Binder 驱动程序发现进程间通信数据中包含有这种 Binder 对象时,就会将对应的文件描述符复制到目标进程中,从而实现在两个进程中共享同一个文件。

2 Ashmem子系统 C/C++ 访问接口分析

在 Android 进程之间共享一个完整的匿名共享内存块,可以通过调用接口 MemoryHeapBase 来实现;在进程之间共享匿名共享内存块中的一部分时,可以通过调用接口 MemoryBase 来实现。

2.1 接口MemoryHeapBase

接口 MemoryBase 以接口 MemoryHeapBase 为基础,这两个接口都可以作为一个 Binder 对象在进程之间进行传输。因为接口 MemoryHeapBase 是一个 Binder 对象,所以拥有 Server 端对象(必须实现一个BnInterface 接口)和 Client 端引用(必须要实现一个 BpInterface 接口)的概念。

2.1.1 在Server端的实现

在 Server 端的实现过程中,接口 MemoryHeapBase 可以将所有涉及到的类分为以下3种类型:

  1. 业务相关类:即跟匿名共享内存操作相关的类,包括 MemoryHeapBase、BnMemoryHeap 和 IMemoryHeap;
  2. Binder 进程通信类:即和 Binder 进程通信机制相关的类,包括 IInterface、BnInterface、IBinder、BBinder、ProcessState 和IPCThreadState;
  3. 智能指针类:RefBase;

在上述3种类型中,Binder 进程通信类和智能指针类可参考专栏:Android系统中的Binder通信机制分析。在接口 IMemoryHeap 中定义了和操作匿名共享内存的几个方法,此接口源码:frameworks/native/include/binder/IMemory.h 中定义

class IMemoryHeap : public IInterface
{
public:
    DECLARE_META_INTERFACE(MemoryHeap);

    // flags returned by getFlags()
    enum {
        READ_ONLY   = 0x00000001,
#ifdef USE_MEMORY_HEAP_ION
        USE_ION_FD  = 0x00008000
#endif
    };

    virtual int         getHeapID() const = 0;
    virtual void*       getBase() const = 0;
    virtual size_t      getSize() const = 0;
    virtual uint32_t    getFlags() const = 0;
    virtual uint32_t    getOffset() const = 0;

    // these are there just for backward source compatibility
    int32_t heapID() const { return getHeapID(); }
    void*   base() const  { return getBase(); }
    size_t  virtualSize() const { return getSize(); }
};

class BnMemoryHeap : public BnInterface<IMemoryHeap>
{
public:
    virtual status_t onTransact(
            uint32_t code,
            const Parcel& data,
            Parcel* reply,
            uint32_t flags = 0);

    BnMemoryHeap();
protected:
    virtual ~BnMemoryHeap();
};

在上述定义代码中,有如下3 个重要的成员函数:

  • getHeapID:功能是获得匿名共享内存块的打开文件描述符;
  • getBase:功能是获得匿名共享内存块的基地址,通过这个地址可以在程序中直接访问这块共享内存;
  • getSize:功能是获得匿名共享内存块的大小;

类 BnMemoryHeap 是一个本地对象类,当 Client 端引用请求 Server 端对象执行命令时,Binder 系统就会调用类 BnMemoryHeap 的成员函数 onTransact 执行具体的命令。函数 onTransact 在源码:frameworks/native/libs/binder/IMemory.cpp 中定义。

status_t BnMemory::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch(code) {
        case GET_MEMORY: {
            CHECK_INTERFACE(IMemory, data, reply);
            ssize_t offset;
            size_t size;
            reply->writeStrongBinder( IInterface::asBinder(getMemory(&offset, &size)) );
            reply->writeInt32(offset);
            reply->writeInt32(size);
            return NO_ERROR;
        } break;
        default:
            return BBinder::onTransact(code, data, reply, flags);
    }
}

类 MemoryHeapBase 继承了类 BnMemoryHeap,作为 Binder 机制中的 Server 角色需要实现 IMemoryBase 接口,主要功能是实现类 IMemoryBase 中列出的成员函数,描述了一块匿名共享内存服务。MemoryHeapBase 类在源码:frameworks/native/include/binder/MemoryHeapBase.h 中定义。

class MemoryHeapBase : public virtual BnMemoryHeap
{
public:
    enum {
        READ_ONLY = IMemoryHeap::READ_ONLY,
        // memory won't be mapped locally, but will be mapped in the remote
        // process.
        DONT_MAP_LOCALLY = 0x00000100,
        NO_CACHING = 0x00000200
    };

    /*
     * maps the memory referenced by fd. but DOESN'T take ownership
     * of the filedescriptor (it makes a copy with dup()
     */
    MemoryHeapBase(int fd, size_t size, uint32_t flags = 0, uint32_t offset = 0);

    /*
     * maps memory from the given device
     */
    MemoryHeapBase(const char* device, size_t size = 0, uint32_t flags = 0);

    /*
     * maps memory from ashmem, with the given name for debugging
     */
    MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = NULL);

    virtual ~MemoryHeapBase();

    /* implement IMemoryHeap interface */
    virtual int         getHeapID() const;

    /* virtual address of the heap. returns MAP_FAILED in case of error */
    virtual void*       getBase() const;

    virtual size_t      getSize() const;
    virtual uint32_t    getFlags() const;
    virtual uint32_t    getOffset() const;

    const char*         getDevice() const;

    /* this closes this heap -- use carefully */
    void dispose();

    /* this is only needed as a workaround, use only if you know
     * what you are doing */
    status_t setDevice(const char* device) {
        if (mDevice == 0)
            mDevice = device;
        return mDevice ? NO_ERROR : ALREADY_EXISTS;
    }

protected:
            MemoryHeapBase();
    // init() takes ownership of fd
    status_t init(int fd, void *base, int size,
            int flags = 0, const char* device = NULL);

private:
    status_t mapfd(int fd, size_t size, uint32_t offset = 0);

    int         mFD;				//一个文件描述符,是在打开设备文件/dev/ashmem后得到的,能够描述一个匿名共享内存块
    size_t      mSize;				//内存块的大小
    void*       mBase;				//内存块的映射地址
    uint32_t    mFlags;				//内存块的访问保护位
    const char* mDevice;
    bool        mNeedUnmap;
    uint32_t    mOffset;
};

类MemoryHeapBase 在源码:frameworks/native/libs/binder/MemoryHeapBase.cpp 中实现,其核心功能是包含了一块匿名共享内存。

MemoryHeapBase::MemoryHeapBase()
    : mFD(-1), mSize(0), mBase(MAP_FAILED),
      mDevice(NULL), mNeedUnmap(false), mOffset(0)
{
}

/*
	size:  表示要创建的匿名共享内存的大小。
	flags: 设置这块匿名共享内存的属性,例如可读写、只读等。
	name:  此参数只是作为调试信息使用的,用于标识匿名共享内存的名字,可以是空值
*/
MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name)
    : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
      mDevice(0), mNeedUnmap(false), mOffset(0)
{
    const size_t pagesize = getpagesize();
    size = ((size + pagesize-1) & ~(pagesize-1));
    int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size);
    ALOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno));
    if (fd >= 0) {
        if (mapfd(fd, size) == NO_ERROR) {
            if (flags & READ_ONLY) {
                ashmem_set_prot_region(fd, PROT_READ);
            }
        }
    }
}

MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags)
    : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
      mDevice(0), mNeedUnmap(false), mOffset(0)
{
    int open_flags = O_RDWR;
    if (flags & NO_CACHING)
        open_flags |= O_SYNC;

    int fd = open(device, open_flags);
    ALOGE_IF(fd<0, "error opening %s: %s", device, strerror(errno));
    if (fd >= 0) {
        const size_t pagesize = getpagesize();
        size = ((size + pagesize-1) & ~(pagesize-1));
        if (mapfd(fd, size) == NO_ERROR) {
            mDevice = device;
        }
    }
}

MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, uint32_t offset)
    : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
      mDevice(0), mNeedUnmap(false), mOffset(0)
{
    const size_t pagesize = getpagesize();
    size = ((size + pagesize-1) & ~(pagesize-1));
    mapfd(dup(fd), size, offset);
}

status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const char* device)
{
    if (mFD != -1) {
        return INVALID_OPERATION;
    }
    mFD = fd;
    mBase = base;
    mSize = size;
    mFlags = flags;
    mDevice = device;
    return NO_ERROR;
}

/*
	其功能是将得到的匿名共享内存的文件描述符映射到进程地址空间。
	在调用函数 mapfd 后,会进入到内核空间的 ashmem 驱动程序模块中执行函数 ashmem_map。
*/
status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset)
{
    if (size == 0) {
        // try to figure out the size automatically
        struct stat sb;
        if (fstat(fd, &sb) == 0)
            size = sb.st_size;
        // if it didn't work, let mmap() fail.
    }

    if ((mFlags & DONT_MAP_LOCALLY) == 0) {		//条件为true 时执行系统调用mmap来执行内存映射的操作
        void* base = (uint8_t*)mmap(0, //表示由内核来决定这个匿名共享内存文件在进程地址空间的起始位置
        							size, //表示要映射的匿名共享内文件的大小
                					PROT_READ|PROT_WRITE, //表示这个匿名共享内存是可读写的
                					MAP_SHARED, 
                					fd, //指定要映射的匿名共享内存的文件描述符
                					offset //表示要从这个文件的哪个偏移位置开始映射
                					);
        if (base == MAP_FAILED) {
            ALOGE("mmap(fd=%d, size=%u) failed (%s)",
                    fd, uint32_t(size), strerror(errno));
            close(fd);
            return -errno;
        }
        //ALOGD("mmap(fd=%d, base=%p, size=%lu)", fd, base, size);
        mBase = base;
        mNeedUnmap = true;
    } else  {
        mBase = 0; // not MAP_FAILED
        mNeedUnmap = false;
    }
    mFD = fd;
    mSize = size;
    mOffset = offset;
    return NO_ERROR;
}

MemoryHeapBase::~MemoryHeapBase()
{
    dispose();
}

void MemoryHeapBase::dispose()
{
    int fd = android_atomic_or(-1, &mFD);
    if (fd >= 0) {
        if (mNeedUnmap) {
            //ALOGD("munmap(fd=%d, base=%p, size=%lu)", fd, mBase, mSize);
            munmap(mBase, mSize);
        }
        mBase = 0;
        mSize = 0;
        close(fd);
    }
}

int MemoryHeapBase::getHeapID() const {
    return mFD;
}

void* MemoryHeapBase::getBase() const {
    return mBase;
}

size_t MemoryHeapBase::getSize() const {
    return mSize;
}

uint32_t MemoryHeapBase::getFlags() const {
    return mFlags;
}

const char* MemoryHeapBase::getDevice() const {
    return mDevice;
}

uint32_t MemoryHeapBase::getOffset() const {
    return mOffset;
}

2.1.2 在Client端的实现

在客户端的实现过程中接口 MemoryHeapBase 可以将所有涉及到的类分为以下3种类型:

  1. 业务相关类:即跟匿名共享内存操作相关的类,包括 BpMemoryHeap、IMemoryHeap;
  2. Binder 进程通信类:即和 Binder 进程通信机制相关的类,包括 IInterface、BnInterface、IBinder、BBinder、ProcessState 和IPCThreadState;
  • 智能指针类:RefBase;

  类 BpMemoryHeap 是类 MemoryHeapBase 在 Client 端进程的远接接口类,当 Client 端进程从 servicemanager 获得了一个 MemoryHeapBase 对象的引用后,会在本地创建一个 BpMemoryHeap 对象来表示这个引用。类 BpMemoryHeap 是从 RefBase 类继承下来的,也要实现 IMemoryHeap 接口,可以和智能指针来结合使用。类 BpMemoryHeap 在源码 frameworks/native/libs/binder/IMemory.cpp 中定义。

class BpMemoryHeap : public BpInterface<IMemoryHeap>
{
public:
    BpMemoryHeap(const sp<IBinder>& impl);
    virtual ~BpMemoryHeap();

    virtual int getHeapID() const;
    virtual void* getBase() const;
    virtual size_t getSize() const;
    virtual uint32_t getFlags() const;
    virtual uint32_t getOffset() const;

private:
    friend class IMemory;
    friend class HeapCache;

    // for debugging in this module
    static inline sp<IMemoryHeap> find_heap(const sp<IBinder>& binder) {
        return gHeapCache->find_heap(binder);
    }
    static inline void free_heap(const sp<IBinder>& binder) {
        gHeapCache->free_heap(binder);
    }
    static inline sp<IMemoryHeap> get_heap(const sp<IBinder>& binder) {
        return gHeapCache->get_heap(binder);
    }
    static inline void dump_heaps() {
        gHeapCache->dump_heaps();
    }

    void assertMapped() const;
    void assertReallyMapped() const;

    mutable volatile int32_t mHeapId;
    mutable void*       mBase;
    mutable size_t      mSize;
    mutable uint32_t    mFlags;
    mutable uint32_t    mOffset;
    mutable bool        mRealHeap;
    mutable Mutex       mLock;
};

类 BpMemoryHeap 对应的构造函数为:

BpMemoryHeap::BpMemoryHeap(const sp<IBinder>& impl)
    : BpInterface<IMemoryHeap>(impl),
        mHeapId(-1), mBase(MAP_FAILED), mSize(0), mFlags(0), mOffset(0), mRealHeap(false)
{
}

成员函数 getHeapID、getBase、getSize、getFlags、getOffset 的实现代码如下所示:

int BpMemoryHeap::getHeapID() const {
    assertMapped();
    return mHeapId;
}

void* BpMemoryHeap::getBase() const {
    assertMapped();
    return mBase;
}

size_t BpMemoryHeap::getSize() const {
    assertMapped();
    return mSize;
}

uint32_t BpMemoryHeap::getFlags() const {
    assertMapped();
    return mFlags;
}

uint32_t BpMemoryHeap::getOffset() const {
    assertMapped();
    return mOffset;
}

  在使用上述成员函数之前,通过调用函数 assertMapped 来确保在 Client 端已经准备好了匿名共享内存。函数 assertMapped 在源码 frameworks/native/libs/binder/IMemory.cpp 中定义。

void BpMemoryHeap::assertMapped() const
{
    if (mHeapId == -1) {
        sp<IBinder> binder(IInterface::asBinder(const_cast<BpMemoryHeap*>(this)));
        sp<BpMemoryHeap> heap(static_cast<BpMemoryHeap*>(find_heap(binder).get()));
        heap->assertReallyMapped();
        if (heap->mBase != MAP_FAILED) {
            Mutex::Autolock _l(mLock);
            if (mHeapId == -1) {
                mBase   = heap->mBase;
                mSize   = heap->mSize;
                mOffset = heap->mOffset;
                android_atomic_write( dup( heap->mHeapId ), &mHeapId );
            }
        } else {
            // something went wrong
            free_heap(binder);
        }
    }
}

类 HeapCache 的定义:

class HeapCache : public IBinder::DeathRecipient
{
public:
    HeapCache();
    virtual ~HeapCache();

    virtual void binderDied(const wp<IBinder>& who);

    sp<IMemoryHeap> find_heap(const sp<IBinder>& binder);
    void free_heap(const sp<IBinder>& binder);
    sp<IMemoryHeap> get_heap(const sp<IBinder>& binder);
    void dump_heaps();

private:
    // For IMemory.cpp
    struct heap_info_t {
        sp<IMemoryHeap> heap;
        int32_t         count;
    };

    void free_heap(const wp<IBinder>& binder);

    Mutex mHeapCacheLock;
    KeyedVector< wp<IBinder>, heap_info_t > mHeapCache;
};

  在上述代码中定义了成员变量 mHeapCache,功能是维护进程内的所有 BpMemoryHeap 对象。另外还提供了函数 find_heap 和函数 get_heap 来查内部所维护的 BpMemoryHeap 对象,这两个函数的具体说明如下所示:

  • 函数 find_heap:如果在 mHeapCache 找不到相应的 BpMemoryHeap 对象,则把 BpMemoryHeap 对象加入到 mHeapCache 中。
  • 函数 get_heap:不会自动把 BpMemoryHeap 对象加入到 mHeapCache 中。

  接下来看函数 find_heap,首先以传进来的参数 binder 作为关键字在 mHeapCache 中查找,查找是否存在对应的 heap_info 对象 info。

  • 如果有:增加引用计数 info.count 的值,表示此 BpBinder 对象多了一个使用者。
  • 如果没有:创建一个放到 mHeapCache 中的 heap_info 对象 info。

函数 find_heap 在源码 frameworks/native/libs/binder/IMemory.cpp 中定义。

sp<IMemoryHeap> HeapCache::find_heap(const sp<IBinder>& binder)
{
    Mutex::Autolock _l(mHeapCacheLock);
    ssize_t i = mHeapCache.indexOfKey(binder);
    if (i>=0) {
        heap_info_t& info = mHeapCache.editValueAt(i);
        ALOGD_IF(VERBOSE,
                "found binder=%p, heap=%p, size=%zu, fd=%d, count=%d",
                binder.get(), info.heap.get(),
                static_cast<BpMemoryHeap*>(info.heap.get())->mSize,
                static_cast<BpMemoryHeap*>(info.heap.get())->mHeapId,
                info.count);
        android_atomic_inc(&info.count);
        return info.heap;
    } else {
        heap_info_t info;
        info.heap = interface_cast<IMemoryHeap>(binder);
        info.count = 1;
        //ALOGD("adding binder=%p, heap=%p, count=%d",
        //      binder.get(), info.heap.get(), info.count);
        mHeapCache.add(binder, info);
        return info.heap;
    }
}

在类 BpMemoryHeap 的定义中有一个成员函数 find_heap,能够调用全局变量 gHeapCache 执行查找的操作。

class BpMemoryHeap : public BpInterface<IMemoryHeap>
{
......
private:
    ......
    // for debugging in this module
    static inline sp<IMemoryHeap> find_heap(const sp<IBinder>& binder) {
        return gHeapCache->find_heap(binder);
    }
}

  通过调用函数 find_heap 得到 BpMemoryHeap 对象中的函数 assertReallyMapped,这样可以确认它内部的匿名共享内存是否已经映射到进程空间。

void BpMemoryHeap::assertReallyMapped() const
{
    if (mHeapId == -1) {

        // remote call without mLock held, worse case scenario, we end up
        // calling transact() from multiple threads, but that's not a problem,
        // only mmap below must be in the critical section.

        Parcel data, reply;
        data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor());
        status_t err = remote()->transact(HEAP_ID, data, &reply);
        int parcel_fd = reply.readFileDescriptor();
        ssize_t size = reply.readInt32();
        uint32_t flags = reply.readInt32();
        uint32_t offset = reply.readInt32();

        ALOGE_IF(err, "binder=%p transaction failed fd=%d, size=%zd, err=%d (%s)",
                IInterface::asBinder(this).get(),
                parcel_fd, size, err, strerror(-err));

#ifdef USE_MEMORY_HEAP_ION
        ion_client ion_client_num = -1;
        if (flags & USE_ION_FD) {
            ion_client_num = ion_client_create();
            ALOGE_IF(ion_client_num < 0, "BpMemoryHeap : ion client creation error");
        }
#endif
        Mutex::Autolock _l(mLock);
        if (mHeapId == -1) {
            int fd = dup( parcel_fd );
            ALOGE_IF(fd==-1, "cannot dup fd=%d, size=%zd, err=%d (%s)",
                    parcel_fd, size, err, strerror(errno));

            int access = PROT_READ;
            if (!(flags & READ_ONLY)) {
                access |= PROT_WRITE;
            }

            mRealHeap = true;

#ifdef USE_MEMORY_HEAP_ION
        if (flags & USE_ION_FD) {
            if (ion_client_num < 0)
                mBase = MAP_FAILED;
            else
                mBase = ion_map(fd, size, offset);
            } else
#endif
            mBase = mmap(0, size, access, MAP_SHARED, fd, offset);
            if (mBase == MAP_FAILED) {
                ALOGE("cannot map BpMemoryHeap (binder=%p), size=%zd, fd=%d (%s)",
                        IInterface::asBinder(this).get(), size, fd, strerror(errno));
                close(fd);
            } else {
                mSize = size;
                mFlags = flags;
                mOffset = offset;
                android_atomic_write(fd, &mHeapId);
            }
        }
#ifdef USE_MEMORY_HEAP_ION
        if (ion_client_num < 0)
            ion_client_num = -1;
        else
            ion_client_destroy(ion_client_num);
#endif
    }
}

2.2 接口 MemoryBase

  接口 MemoryBase 是建立在接口 MemoryHeapBase 基础上的,两者都可以作为一个 Binder 对象在进程之间实现数据共享。

2.2.1 在Server端的实现

  首先分析类 MemoryBase 在 Server 端的实现,MemoryBase 在 Server 端只是简单地封装了 MemoryHeapBase 的实现。类 MemoryBase 在 Server 端的实现跟类 MemoryHeapBase 在 Server端的实现类似,只需在整个类图结构中实现如下转换即可:

  • 把类 IMemory 换成类 ImemoryHeap;
  • 把类 BnMemory 换成类 BnMemoryHeap;
  • 把类 MemoryBase 换成类 MemoryHeapBase;

  类 IMemory 在源码:frameworks/native/include/binder/IMemory.h 中实现,功能是定义类 MemoryBase 所需要的实现接口。

class IMemory : public IInterface
{
public:
    DECLARE_META_INTERFACE(Memory);

    virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const = 0;

    // helpers
    void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const;
    void* pointer() const;
    size_t size() const;
    ssize_t offset() const;
};

在类 IMemory 中定义了如下所示的成员函数:

  • getMemory:功能是获取内部 MemoryHeapBase 对象的 IMemoryHeap 接口;
  • pointer():功能是获取内部所维护的匿名共享内存的基地址;
  • size():功能是获取内部所维护的匿名共享内存的大小;
  • offset():功能是获取内部所维护的匿名共享内存在整个匿名共享内存中的偏移量。

  类 IMemory 在本身定义过程中实现了3个成员函数:pointer、size 和 offset,其子类 MemoryBase 只需实现成员函数 getMemory 即可。类 IMemory 的具体实现在源码 frameworks/native/libs/binder/IMemory.cpp 中定义。

void* IMemory::pointer() const {
    ssize_t offset;
    sp<IMemoryHeap> heap = getMemory(&offset);
    void* const base = heap!=0 ? heap->base() : MAP_FAILED;
    if (base == MAP_FAILED)
        return 0;
    return static_cast<char*>(base) + offset;
}

size_t IMemory::size() const {
    size_t size;
    getMemory(NULL, &size);
    return size;
}

ssize_t IMemory::offset() const {
    ssize_t offset;
    getMemory(&offset);
    return offset;
}

  类 MemoryBase 是一个本地 Binder 对象类在源码 frameworks/native/include/binder/MemoryBase.h 中声明,具体实现代码如下所示:

namespace android {

// ---------------------------------------------------------------------------

class MemoryBase : public BnMemory
{
public:
    MemoryBase(const sp<IMemoryHeap>& heap, ssize_t offset, size_t size);
    virtual ~MemoryBase();
    virtual sp<IMemoryHeap> getMemory(ssize_t* offset, size_t* size) const;

protected:
    size_t getSize() const { return mSize; }
    ssize_t getOffset() const { return mOffset; }
    const sp<IMemoryHeap>& getHeap() const { return mHeap; }

private:
    size_t          mSize;
    ssize_t         mOffset;
    sp<IMemoryHeap> mHeap;
};

// ---------------------------------------------------------------------------
}; // namespace android

类 MemoryBase 的具体实现在源码 frameworks/native/libs/binder/IMemory.cpp 中定义。

namespace android {

MemoryBase::MemoryBase(const sp<IMemoryHeap>& heap, //指向 MemoryHeapBase 对象,真正的匿名共享内存就是由它来维护的
        ssize_t offset, size_t size) //表示这个MemoryBase 对象所要维护的这部分匿名共享内存在整个匿名共享内存块中的起始位置
    : mSize(size), mOffset(offset), mHeap(heap) //表示这个MemoryBase 对象所要维护的这部分匿名共享内存的大小
{
}

/*
	功能是返回内部的 MemoryHeapBase 对象的 IMemoryHeap 接口
	如果传进来的参数offset 和 size 不为 NULL
	会把其内部维护的这部分匿名共享内存,在整个匿名共享内存块中的偏移位置
	以及这部分匿名共享内存的大小返回给调用者
*/
sp<IMemoryHeap> MemoryBase::getMemory(ssize_t* offset, size_t* size) const
{
    if (offset) *offset = mOffset;
    if (size)   *size = mSize;
    return mHeap;
}

MemoryBase::~MemoryBase()
{
}

}; // namespace android

2.2.2 在Client端的实现

  MemoryBase 类在 Client 端的实现,类 MemoryBase 在 Client 端的实现与类 MemoryHeapBase 在 Client 端的实现类似,只需要进行如下所示的类转换即可成为 MemoryHeapBase 在 Client端的实现。

  • 把类 IMemory 换成类 ImemoryHeap;
  • 把类 BpMemory 换成类 BpMemoryHeap;

  类 BpMemory 用于描述类 MemoryBase 服务的代理对象,在源码 frameworks/native/libs/binder/IMemory.cpp 中定义。

class BpMemory : public BpInterface<IMemory>
{
public:
    BpMemory(const sp<IBinder>& impl);
    virtual ~BpMemory();
    virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const;

private:
    mutable sp<IMemoryHeap> mHeap;
    mutable ssize_t mOffset;
    mutable size_t mSize;
};

类 BpMemory 中的成员函数 getMemory 在源码 frameworks/native/libs/binder/IMemory.cpp 中定义。

sp<IMemoryHeap> BpMemory::getMemory(ssize_t* offset, size_t* size) const
{
    if (mHeap == 0) {
        Parcel data, reply;
        data.writeInterfaceToken(IMemory::getInterfaceDescriptor());
        if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) {
            sp<IBinder> heap = reply.readStrongBinder();
            ssize_t o = reply.readInt32();
            size_t s = reply.readInt32();
            if (heap != 0) {
                mHeap = interface_cast<IMemoryHeap>(heap);
                if (mHeap != 0) {
                    size_t heapSize = mHeap->getSize();
                    if (s <= heapSize
                            && o >= 0
                            && (static_cast<size_t>(o) <= heapSize - s)) {
                        mOffset = o;
                        mSize = s;
                    } else {
                        // Hm.
                        android_errorWriteWithInfoLog(0x534e4554,
                            "26877992", -1, NULL, 0);
                        mOffset = 0;
                        mSize = 0;
                    }
                }
            }
        }
    }
    if (offset) *offset = mOffset;
    if (size) *size = mSize;
    return (mSize > 0) ? mHeap : 0;
}

  如果成员变量 mHeap 的值为 NULL,表示此 BpMemory 对象还没有建立好名共享内存,此时会调用一个 Binder 进程去 Server 端请求匿名共享内存信息。通过引用信息中的 Server 端的 MemoryHeapBase 对象的引用 heap,可以在 Client 端进程中创建一个 BpMemoryHeap 远程接口,最后将这个 BpMemoryHeap 远程接口保存在成员变量 mHeap 中,同时从 Server 端获得的信息还包括这块匿名共享内存在整个匿名共享内存中的偏移位置以及大小。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/617274.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于深度学习的高精度人体摔倒行为检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度人体摔倒行为检测识别系统可用于日常生活中或野外来检测与定位人体摔倒行为目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的人体摔倒行为目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系…

前端学习--Vue3.0(1)

1使用create-vue搭建Vue3项目 1.1 认识create-vue create-vue是Vue官方新的脚手架工具&#xff0c;底层切换到了 vite&#xff08;下一代前端工具链&#xff09;&#xff0c;为开发提供极速响应 create-vue基于vite vue-cli基于webpack 1.2 创建项目 需要16.0及以上的node.j…

机器学习笔记:深度学习模型复杂度

1 时间复杂度&#xff08;模型运算次数&#xff09; 决定了模型的训练/预测时间用FLOPs指代 机器学习笔记&#xff1a;FLOPs_flop 机器学习_UQI-LIUWJ的博客-CSDN博客 2 空间复杂度&#xff08;模型参数数量&#xff09; 决定了模型的参数数量 2.1 全连接层参数量 包含bias…

EBU6304 Software Engineering 知识点总结_6 项目管理_下 Design Patterns

Design Patterns Decorator Design pattern 用于扩展系统功能的装饰模式。 比如我们有一个鸭子类&#xff0c;可以调用其鸭子叫的方法。我们初始化了一个鸭子对象&#xff0c;如何统计这个对象叫了几次&#xff0c;也就是其鸭子叫方法被调用了几次&#xff1f;记住OCP原则不…

mysql 在 linux下的安装 和 配置

文章目录 1. linux 安装mysql1. 源码安装1.找到源码包2&#xff1a; 进行配置3&#xff1a;初始化数据目录官方说明 自用4&#xff1a;启动数据库5:写入环境变量 2&#xff1a;使用yum安装1. 直接配置mysql仓库 或者下载MySQL Yum Repository2&#xff1a;安装启动进入mysql 3&…

整理推荐 6 个超好用的平面设计网站!

本文将为大家介绍了 6 个最佳学习平面设计的网站&#xff0c;包括即时设计、Behance、Awwwards、Dribbble、Designinspiration 和 Pinterest。选择学习平面设计网站需要考虑匹配自己的设计风格、是否具有局限性等&#xff0c;可以试用几个网站后选择最合适的。 1、即时设计 即…

我们投出去的简历为什么已读不回?来看面试官如何筛选简历、挑选求职者?

目录 前言&#xff1a; 应届生 1、看学历 2、看实习经历 3、看专业 职场人士 面试官喜欢问什么问题 总结 前言&#xff1a; 金三银四&#xff0c;是求职者蠢蠢欲动的季节&#xff0c;亦是企业摩拳擦掌的季节。 因为作为企业的一名金牌面试官&#xff0c;我收到的内推…

RK1126 C++ yolov5 6.2

基于 rk npu &#xff0c; 实现 yolov5 6.2 模型推理 实现过程 ⚡️​ 编译 opencv 需根据自己路径修改. cmake -D CMAKE_BUILD_TYPERELEASE \-D CMAKE_C_COMPILER./gcc-arm-8.3-2019.02-x86_64-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc \-D CMAKE_CXX_COMPILER./gc…

【Linux】多线程02 --- 线程的同步互斥问题及生产消费模型

&#x1f34e;作者&#xff1a;阿润菜菜 &#x1f4d6;专栏&#xff1a;Linux系统编程 目录 一、线程互斥1. 为什么要有共享资源临界保护&#xff1f; 2.理解加锁2.1 认识锁&#xff0c;使用锁 线程同步互斥问题是指多线程程序中&#xff0c;如何保证共享资源的正确访问和线程间…

阿里P8大佬七天七夜制作这份自动化核心知识点,错过了就是错过了

整理了一份自动化核心知识点。覆盖了web前端基础&#xff0c;HTML标签&#xff0c;CSS样式&#xff0c;自动化测试工具&#xff0c;webdriver环境搭建&#xff0c;元素定位&#xff0c;手机操作系统&#xff0c;移动自动化测试工具&#xff0c;自动化测试的流程与分类&#xff…

web自动化测试入门篇01——框架介绍

1. 目的 web自动化测试作为软件自动化测试领域中绕不过去的一个“香饽饽”&#xff0c;通常都会作为广大测试从业者的首选学习对象&#xff0c;相较于C/S架构的自动化来说&#xff0c;B/S有着其无法忽视的诸多优势&#xff0c;从行业发展趋、研发模式特点、测试工具支持&#x…

高完整性系统(7)Formal Verification and Validation

文章目录 Specification Process 规格化过程State Invariants案例check ... expect Alloy是一种用于构建和检查抽象模型的语言和工具。当Alloy说所有断言都成立时&#xff0c;这意味着你的模型或规格在给定范围内已成功通过了所有的断言检查。换句话说&#xff0c;对于你所定义…

SOLIDWORKS PDM 独立程序 C#

本主题介绍如何创建登录到 一个 SOLIDWORKS PDM Professional 文件库&#xff0c;并列出根文件夹中的文件。 启动Visual Studio.文件 > 新建 > 项目 > Visual C# > WPF&#xff08;也可以使用WF&#xff09; 输入程序名称选择存储路径确定在解决方案资源管理器中…

(学习日记)2023.06.06

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

【Linux】基础文件IO、动静态库的制作和使用

基础IO 前言回顾C语言文件IO操作三个标准 系统文件I/O系统调用接口不带mode的open带mode的openwirtereadopen的第二个参数flagsopen返回值文件的管理0&#xff0c;1&#xff0c;2演示文件描述符的分配规则重定向C中的0、1、2输入重定向追加重定向 另一种重定向的方式dup2实现输…

MySQL5.7主从同步配置(一台master,两台slave)

1. 下载MySQL(5.7.42) rpm -ivh http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm2.安装 yum install mysql-server2.1 安装过程中如果报错如下&#xff0c;按下边方法处理。否则略过即可 2.2 解决方案&#xff1a;执行以下命令 rpm --import https…

mongodb redis mysql 区别

一、MySQL 关系型数据库。 在不同的引擎上有不同 的存储方式。 查询语句是使用传统的sql语句&#xff0c;拥有较为成熟的体系&#xff0c;成熟度很高。 开源数据库的份额在不断增加&#xff0c;mysql的份额页在持续增长。 缺点就是在海量数据处理的时候效率会显著变慢。 二、Mo…

Vue3中setup函数、以及父子组件传值讲解

文章目录 1.vue3中setup函数的执行时机2.setup函数的两种写法2.1 普通写法2.2 语法糖写法 3.vue3父组件给子组件传值。4.vue3子组件给父组件传值 1.vue3中setup函数的执行时机 setup选项的写法和执行时机&#xff0c;setup函数在beforeCreate函数之前执行&#xff0c;并且是自…

以安全为底线 共迎机遇和挑战|2023 开放原子全球开源峰会可信基础设施技术分论坛即将启幕

蚂蚁集团的业务领域&#xff0c;对于「可信」有非常高的技术要求。这种可信技术不仅体现在可靠、健壮&#xff0c;也体现在金融领域独有的风控难题以及分布式系统中持续提供服务的续航能力。可信基础设施中有大量的开源项目&#xff0c;而新的机会也在不断涌现。 2023 开放原子…

echarts 图表导出PDF(带滚动条)/图片导出PDF

echarts 图表导出PDF[带滚动条]/图片导出PDF 效果展示提出问题思考问题解决问题导出PDF 里面的页头中文乱码问题参数说明 效果展示 提出问题 在开发过程中,有需求是将展示出来的echarts图表导出为pdf 原本我的滚动条是使用echarts图表进行的滚动,但通过了解后得知,echarts图表如…