Ashmem anonymous shared memory subsystem analysis of Android system (3) - C/C++ access interface of Ashmem subsystem

statement

1 Architecture of Ashmem

  The Ashmem anonymous shared memory subsystem implemented by the Android system is used to share data between applications. Ashmem, like the shared memory implemented by the traditional Linux system, is implemented based on the temporary file system tmpfs provided by the kernel, but Ashmem manages the memory blocks more finely. The application program can dynamically divide an anonymous shared memory into several small blocks. When these small blocks of memory are no longer needed, they can be reclaimed by the memory management system. Through this dynamic, divide-and-conquer memory management method, the Android system can effectively use the system memory and adapt to the mobile device environment with small memory.

  The anonymous shared memory system is based on the Ashmem driver. All anonymous shared memory in the system is allocated and managed by the Ashmem driver. The Android system provides a C/C++ call interface at the Native layer and a Java call interface at the Framework layer.

  • In the Framework layer, two C++ classes MemoryBase and MemoryHeapBase, and a Java class MemoryFile are provided to use anonymous shared memory.
  • In the runtime library cutils, three C functions ashmem_create_region, ashmem_pin_region and ashmem_unpin_region are mainly provided to access the Ashmem driver.

  When the Ashmem driver starts, it will create a /dev/ashmem device file, so that the anonymous shared memory interface in the runtime library cutils can access the Ashmem driver through the file operation functions open and ioctl.
insert image description here

  The traditional Linux system uses an integer to mark a piece of shared memory, while the Android system uses a file descriptor to mark a piece of anonymous shared memory. Using a file descriptor to describe an anonymous shared memory has two advantages:

  1. It can be conveniently mapped into the address space of the process, allowing direct access to its contents;
  2. You can use the Binder inter-process communication mechanism to transfer this file descriptor, so as to share an anonymous memory between different applications.

  The Binder inter-process communication mechanism uses a Binder object of type BINDER_TYPE_FD to describe a file descriptor. When the Binder driver finds that the inter-process communication data contains such a Binder object, it will copy the corresponding file descriptor to the target process. , so as to share the same file in two processes.

2 Ashmem subsystem C/C++ access interface analysis

Sharing a complete anonymous shared memory block between Android processes can be realized by calling the interface MemoryHeapBase; when sharing a part of the anonymous shared memory block between processes, it can be realized by calling the interface MemoryBase.

2.1 Interface MemoryHeapBase

The interface MemoryBase is based on the interface MemoryHeapBase, and both interfaces can be transferred between processes as a Binder object. Because the interface MemoryHeapBase is a Binder object, it has the concepts of Server-side object (must realize a BnInterface interface) and Client-side reference (must realize a BpInterface interface).

2.1.1 Implementation on the Server side

During the implementation process on the server side, the interface MemoryHeapBase can divide all involved classes into the following three types:

  1. Business-related classes: classes related to anonymous shared memory operations, including MemoryHeapBase, BnMemoryHeap, and IMemoryHeap;
  2. Binder process communication class: the class related to the Binder process communication mechanism, including IInterface, BnInterface, IBinder, BBinder, ProcessState and IPCThreadState;
  3. Smart pointer class: RefBase;

Among the above three types, Binder process communication class and smart pointer class can refer to the column: Analysis of Binder communication mechanism in Android system . Several methods for operating anonymous shared memory are defined in the interface IMemoryHeap. The source code of this interface is defined in 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();
};

In the above definition code, there are three important member functions as follows:

  • getHeapID: The function is to obtain the open file descriptor of the anonymous shared memory block;
  • getBase: The function is to obtain the base address of the anonymous shared memory block, through which the shared memory can be directly accessed in the program;
  • getSize: The function is to obtain the size of the anonymous shared memory block;

The class BnMemoryHeap is a local object class. When the client references and requests the server object to execute the command, the Binder system will call the member function onTransact of the class BnMemoryHeap to execute the specific command. The function onTransact is defined in the source code: 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);
    }
}

The class MemoryHeapBase inherits the class BnMemoryHeap. As the Server role in the Binder mechanism, it needs to implement the IMemoryBase interface. The main function is to implement the member functions listed in the class IMemoryBase, which describes an anonymous shared memory service. The MemoryHeapBase class is defined in the source code: 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;
};

The class MemoryHeapBase is implemented in the source code: frameworks/native/libs/binder/MemoryHeapBase.cpp, and its core function is to include an anonymous shared memory.

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 Implementation on the Client side

In the implementation process of the client, the interface MemoryHeapBase can divide all involved classes into the following three types:

  1. Business-related classes: classes related to anonymous shared memory operations, including BpMemoryHeap and IMemoryHeap;
  2. Binder process communication class: the class related to the Binder process communication mechanism, including IInterface, BnInterface, IBinder, BBinder, ProcessState and IPCThreadState;
  • Smart pointer class: RefBase;

  The class BpMemoryHeap is the remote interface class of the class MemoryHeapBase in the client-side process. When the client-side process obtains a reference of a MemoryHeapBase object from the servicemanager, it will create a BpMemoryHeap object locally to represent the reference. The class BpMemoryHeap is inherited from the RefBase class and also implements the IMemoryHeap interface, which can be used in combination with smart pointers. The class BpMemoryHeap is defined in the source code 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;
};

The corresponding constructor of class BpMemoryHeap is:

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

The implementation code of the member functions getHeapID, getBase, getSize, getFlags, getOffset is as follows:

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;
}

  Before using the above member functions, call the function assertMapped to ensure that the anonymous shared memory is ready on the client side. The function assertMapped is defined in the source code 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);
        }
    }
}

Definition of class 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;
};

  The member variable mHeapCache is defined in the above code, and its function is to maintain all BpMemoryHeap objects in the process. In addition, the function find_heap and function get_heap are provided to check the internally maintained BpMemoryHeap object. The specific description of these two functions is as follows:

  • Function find_heap: If the corresponding BpMemoryHeap object cannot be found in mHeapCache, add the BpMemoryHeap object to mHeapCache.
  • Function get_heap: will not automatically add BpMemoryHeap object to mHeapCache.

  Next, look at the function find_heap. First, use the parameter binder passed in as a keyword to search in mHeapCache to find out whether there is a corresponding heap_info object info.

  • If yes: increase the value of the reference count info.count, indicating that this BpBinder object has one more user.
  • If not: Create a heap_info object info that is placed in mHeapCache.

The function find_heap is defined in the source code 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;
    }
}

There is a member function find_heap in the definition of class BpMemoryHeap, which can call the global variable gHeapCache to perform the search operation.

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);
    }
}

  Get the function assertReallyMapped in the BpMemoryHeap object by calling the function find_heap, so you can confirm whether the anonymous shared memory inside it has been mapped to the process space.

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 Interface MemoryBase

  The interface MemoryBase is based on the interface MemoryHeapBase, both of which can be used as a Binder object to share data between processes.

2.2.1 Implementation on the Server side

  Firstly, analyze the implementation of the class MemoryBase on the server side. MemoryBase simply encapsulates the implementation of MemoryHeapBase on the server side. The implementation of the class MemoryBase on the server side is similar to the implementation of the class MemoryHeapBase on the server side, only need to implement the following conversion in the entire class diagram structure:

  • Replace the class IMemory with the class IMemoryHeap;
  • Replace class BnMemory with class BnMemoryHeap;
  • Replace class MemoryBase with class MemoryHeapBase;

  The class IMemory is implemented in the source code: frameworks/native/include/binder/IMemory.h, and the function is to define the implementation interface required by the class 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;
};

The following member functions are defined in class IMemory:

  • getMemory: The function is to obtain the IMemoryHeap interface of the internal MemoryHeapBase object;
  • pointer(): The function is to obtain the base address of the anonymous shared memory maintained internally;
  • size(): The function is to obtain the size of the anonymous shared memory maintained internally;
  • offset(): The function is to obtain the offset of the internally maintained anonymous shared memory in the entire anonymous shared memory.

  The class IMemory implements three member functions in its own definition process: pointer, size and offset, and its subclass MemoryBase only needs to implement the member function getMemory. The specific implementation of the class IMemory is defined in the source code 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;
}

  Class MemoryBase is a local Binder object class declared in the source code frameworks/native/include/binder/MemoryBase.h, and the specific implementation code is as follows:

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

The specific implementation of class MemoryBase is defined in the source code 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 Implementation on the Client side

  The implementation of the MemoryBase class on the client side. The implementation of the class MemoryBase on the client side is similar to the implementation of the class MemoryHeapBase on the client side. It only needs to perform the class conversion as shown below to become the implementation of MemoryHeapBase on the client side.

  • Replace the class IMemory with the class IMemoryHeap;
  • Replace class BpMemory with class BpMemoryHeap;

  The class BpMemory is used to describe the proxy object of the class MemoryBase service, which is defined in the source code 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;
};

The member function getMemory in the class BpMemory is defined in the source code 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;
}

  If the value of the member variable mHeap is NULL, it means that the BpMemory object has not established a named shared memory. At this time, a Binder process will be called to request anonymous shared memory information from the Server. Through the reference heap of the MemoryHeapBase object on the server side in the reference information, a BpMemoryHeap remote interface can be created in the client process, and finally this BpMemoryHeap remote interface is saved in the member variable mHeap, and the information obtained from the server side also includes this anonymous The offset position and size of the shared memory in the entire anonymous shared memory.

Guess you like

Origin blog.csdn.net/Xiaoma_Pedro/article/details/131057819