HIDL MemoryBlock

Das HIDL MemoryBlock ist eine abstrakte Ebene, die auf hidl_memory, HIDL @1.0::IAllocator und HIDL @1.0::IMapper basiert. Es ist für HIDL-Dienste konzipiert, die mehrere Speicherblöcke haben, um einen einzelnen Speicher-Heap zu teilen.

Leistungsverbesserungen

Die Verwendung von MemoryBlock in Apps kann die Anzahl der mmap-/munmap- und Nutzerbereichs-Segmentierungsfehler erheblich reduzieren und so die Leistung verbessern. Beispiel:

  • Die Verwendung von „per hidl_memory“ für jede Pufferzuweisung dauert durchschnittlich 238 Mikrosekunden pro Zuweisung.
  • Bei Verwendung von MemoryBlock und Freigabe einer einzelnen hidl_memory sind durchschnittlich 2,82 µs/Zuweisung erforderlich.

Architektur

Die HIDL-Architektur MemoryBlock umfasst HIDL-Dienste mit mehreren Speicherblöcken, die sich einen einzelnen 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. Dazu wird zuerst die HAL deklariert und dann implementiert.

HAL deklarieren

Für das folgende IFoo-HAL-Beispiel:

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

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

Der Android.bp sieht so aus:

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

HAL implementieren

So implementieren Sie das Beispiel-HAL:

  1. Rufen Sie die hidl_memory ab (weitere Informationen finden Sie unter HIDL 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. Erstellen Sie 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. Dies ist eine mit HIDL definierte Struktur.

    Beispiel MemoryBlock:

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

    Beispiel für die Zuweisung einer MemoryBlock mit 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 freigeben:

    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. Konfiguration 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 die Referenzanzahl, um die gemeinsam genutzte hidl_memory beizubehalten, die beim ersten Mal, wenn eine ihrer 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 Sperrlebenszyklus zugeordnet wird, mmap()-ed wird. Beispiel:

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

Verlängerte Nutzung

Dieser Abschnitt enthält Details zur erweiterten Nutzung von MemoryBlock.

Verwenden der Referenzanzahl zum Verwalten von MemoryBlock

In den meisten Situationen ist es am effizientesten, MemoryBlock explizit zuzuweisen bzw. freizugeben. Bei komplexen Apps kann es jedoch besser sein, die Referenzzählung für die Garbage Collection zu verwenden. Um die Referenzanzahl für MemoryBlock zu ermitteln, können Sie MemoryBlock an ein Binder-Objekt binden. So können die Referenzen gezählt und MemoryBlock freigegeben werden, wenn die Anzahl auf null sinkt.

HAL deklarieren

Beschreiben Sie beim Deklarieren des 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;
};

Verwenden Sie MemoryBlockAllocation, um MemoryBlock zu ersetzen, und entfernen Sie die Methode, um MemoryBlock zurückzugeben. Der Speicher wird durch Referenzzählung mit MemoryBlockAllocation freigegeben. Beispiel:

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

HAL implementieren

Beispiel für die serverseitige Implementierung des 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 mit dem zugewiesenen MemoryBlock zu verknüpfen. Sie haben zwei Möglichkeiten, Metadaten anzuhängen und abzurufen:

  • Wenn die App so oft auf die Metadaten zugreift wie auf den Block selbst, hängen Sie die Metadaten an und übergeben Sie sie alle in einer Struktur. 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 der Block, ist es effizienter, die Metadaten passiv über eine Schnittstelle zu übergeben. Beispiel:

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

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

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