HIDL MemoryBlock

HIDL MemoryBlock ist eine abstrakte Schicht, die auf hidl_memory, HIDL @1.0::IAllocator und HIDL @1.0::IMapper basiert. Sie ist für HIDL-Dienste mit mehreren Speicherblöcken konzipiert, die einen gemeinsamen Speicher-Heap teilen.

Leistungsverbesserungen

Durch die Verwendung von MemoryBlock in Apps kann die Anzahl der mmap/munmap- und User-Space-Segmentierungsfehler erheblich reduziert und so die Leistung verbessert werden. Beispiel:

  • Bei der Verwendung von „pro hidl_memory“ für jede Pufferzuordnung beträgt die durchschnittliche Wartezeit 238 µs/1 Zuordnung.
  • Wenn Sie MemoryBlock verwenden und eine einzelne hidl_memory freigeben, beträgt die durchschnittliche Ausführungszeit 2,82 µs/1 Zuweisung.

Architektur

Die HIDL MemoryBlock-Architektur umfasst HIDL-Dienste mit mehreren Speicherblöcken, die sich einen einzigen Speicher-Heap teilen:

HIDL MemoryBlock

Abbildung 1: HIDL MemoryBlock-Architektur

Normale Nutzung

In diesem Abschnitt wird ein Beispiel für die Verwendung von MemoryBlock gezeigt. Dabei wird zuerst die HAL deklariert und dann implementiert.

HAL deklarieren

Für das folgende Beispiel-HAL IFoo:

import android.hidl.memory.block@1.0::MemoryBlock;

interface IFoo {
    getSome() generates(MemoryBlock block);
    giveBack(MemoryBlock block);
};

Der Android.bp ist so aufgebaut:

hidl_interface {
    ...
    srcs: [
        "IFoo.hal",
    ],
    interfaces: [
        "android.hidl.memory.block@1.0",
        ...
};

HAL implementieren

So implementieren Sie die Beispiel-HAL:

  1. Rufen Sie den hidl_memory ab. Weitere Informationen finden Sie unter HIDL für C++.

    #include <android/hidl/allocator/1.0/IAllocator.h>
    
    using ::android::hidl::allocator::V1_0::IAllocator;
    using ::android::hardware::hidl_memory;
    ...
      sp<IAllocator> allocator = IAllocator::getService("ashmem");
      allocator->allocate(2048, [&](bool success, const hidl_memory& mem)
      {
            if (!success) { /* error */ }
            // you can now use the hidl_memory object 'mem' or pass it
      }));
    
  2. Erstelle eine HidlMemoryDealer-Instanz mit dem abgerufenen hidl_memory:

    #include <hidlmemory/HidlMemoryDealer.h>
    
    using ::android::hardware::HidlMemoryDealer
    /* The mem argument is acquired in the Step1, returned by the ashmemAllocator->allocate */
    sp<HidlMemoryDealer> memory_dealer = HidlMemoryDealer::getInstance(mem);
    
  3. Weisen Sie MemoryBlock zu, einem mit HIDL definierten Typ.

    Beispiel MemoryBlock:

    struct MemoryBlock {
    IMemoryToken token;
    uint64_t size;
    uint64_t offset;
    };
    

    Beispiel für die Zuweisung eines MemoryBlock mithilfe der MemoryDealer:

    #include <android/hidl/memory/block/1.0/types.h>
    
    using ::android::hidl::memory::block::V1_0::MemoryBlock;
    
    Return<void> Foo::getSome(getSome_cb _hidl_cb) {
        MemoryBlock block = memory_dealer->allocate(1024);
        if(HidlMemoryDealer::isOk(block)){
            _hidl_cb(block);
        ...
    
  4. MemoryBlock nicht mehr zuweisen:

    Return<void> Foo::giveBack(const MemoryBlock& block) {
        memory_dealer->deallocate(block.offset);
    ...
    
  5. Daten bearbeiten:

    #include <hidlmemory/mapping.h>
    #include <android/hidl/memory/1.0/IMemory.h>
    
    using ::android::hidl::memory::V1_0::IMemory;
    
    sp<IMemory> memory = mapMemory(block);
    uint8_t* data =
    
    static_cast<uint8_t*>(static_cast<void*>(memory->getPointer()));
    
  6. Config Android.bp:

    shared_libs: [
            "android.hidl.memory@1.0",
    
            "android.hidl.memory.block@1.0"
    
            "android.hidl.memory.token@1.0",
            "libhidlbase",
            "libhidlmemory",
    
  7. Prüfen Sie den Ablauf, um festzustellen, ob Sie lockMemory müssen.

    Normalerweise verwendet MemoryBlock eine Referenzzählung, um den freigegebenen hidl_memory zu verwalten, der zum ersten Mal mmap() wird, wenn einer seiner MemoryBlock instances is mapped and ismunmap()-ed when nothing refers to it. To keephidl_memoryalways mapped, you can uselockMemory, a RAII style object that keeps the correspondinghidl_memory` während des gesamten Lebenszyklus der Sperre zugeordnet wird. Beispiel:

    #include <hidlmemory/mapping.h>
    
    sp<RefBase> lockMemory(const sp<IMemoryToken> key);
    

Erweiterte Nutzung

In diesem Abschnitt finden Sie Details zur erweiterten Verwendung von MemoryBlock.

Referenzzählung zum Verwalten von MemoryBlock verwenden

In den meisten Fällen ist die effizienteste Verwendung von MemoryBlock die explizite Zuweisung/Deaktivierung. Bei komplexen Apps ist die Verwendung der Referenzzählung für die Garbage Collection jedoch möglicherweise eine bessere Idee. Wenn Sie eine Referenzzählung für MemoryBlock haben möchten, können Sie MemoryBlock an ein Binder-Objekt binden. Dies hilft, die Verweise zu zählen und MemoryBlock zu deallokieren, wenn die Anzahl auf null sinkt.

HAL deklarieren

Deklarieren Sie bei der Deklarierung der HAL eine HIDL-Struktur, die eine MemoryBlock-Instanz und eine IBase enthält:

import android.hidl.memory.block@1.0::MemoryBlock;

struct MemoryBlockAllocation {
    MemoryBlock block;
    IBase refcnt;
};

Ersetzen Sie MemoryBlock durch MemoryBlockAllocation und entfernen Sie die Methode, um MemoryBlock wiederherzustellen. Die Deaktivierung erfolgt durch Referenzzählung mit MemoryBlockAllocation. Beispiel:

interface IFoo {
    allocateSome() generates(MemoryBlockAllocation allocation);
};

HAL implementieren

Beispiel für die dienstseitige Implementierung der HAL:

class MemoryBlockRefCnt: public virtual IBase {
   MemoryBlockRefCnt(uint64_t offset, sp<MemoryDealer> dealer)
     : mOffset(offset), mDealer(dealer) {}
   ~MemoryBlockRefCnt() {
       mDealer->deallocate(mOffset);
   }
 private:
   uint64_t mOffset;
   sp<MemoryDealer> mDealer;
};

Return<void> Foo::allocateSome(allocateSome_cb _hidl_cb) {
    MemoryBlockAllocation allocation;
    allocation.block = memory_dealer->allocate(1024);
    if(HidlMemoryDealer::isOk(block)){
        allocation.refcnt= new MemoryBlockRefCnt(...);
        _hidl_cb(allocation);

Beispiel für die clientseitige Implementierung der HAL:

ifoo->allocateSome([&](const MemoryBlockAllocation& allocation){
    ...
);

Metadaten anhängen und abrufen

Für einige Apps sind zusätzliche Daten erforderlich, um sie an die zugewiesene MemoryBlock zu binden. Es gibt zwei Möglichkeiten, Metadaten anzuhängen und abzurufen:

  • Wenn die App genauso oft auf die Metadaten zugreift wie auf den Block selbst, fügen Sie die Metadaten an und übergeben Sie sie alle in einem String. Beispiel:

    import android.hidl.memory.block@1.0::MemoryBlock;
    
    struct MemoryBlockWithMetaData{
        MemoryBlock block;
        MetaDataStruct metaData;
    };
    
  • Wenn die App viel seltener auf die Metadaten zugreift als auf den Block, ist es effizienter, die Metadaten passiv über eine Benutzeroberfläche zu übergeben. Beispiel:

    import android.hidl.memory.block@1.0::MemoryBlock;
    
    struct MemoryBlockWithMetaData{
        MemoryBlock block;
        IMetaData metaData;
    };
    

    Binden Sie als Nächstes die Metadaten mithilfe von MemoryDealer an die MemoryBlock. Beispiel:

    MemoryBlockWithMetaData memory_block;
    memory_block.block = dealer->allocate(size);
    if(HidlMemoryDealer::isOk(block)){
        memory_block.metaData = new MetaData(...);