HIDL MemoryBlock

HIDL MemoryBlock ist eine abstrakte Schicht, die auf hidl_memory, HIDL @1.0::IAllocator und HIDL @1.0::IMapper basiert. Es wurde für HIDL-Dienste entwickelt, die mehrere Speicherblöcke haben, um einen einzelnen Arbeitsspeicher-Heap gemeinsam zu nutzen.

Leistungsverbesserungen

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

  • Bei 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 Arbeitsspeicherblöcken, die sich einen einzelnen Arbeitsspeicher-Heap teilen:

HIDL MemoryBlock

Abbildung 1. HIDL MemoryBlock-Architektur

Normale Nutzung

Dieser Abschnitt enthält ein Beispiel für die Verwendung von MemoryBlock, bei dem zuerst der HAL deklariert und dann der HAL implementiert wird.

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 das 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. 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 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 manipulieren:

    #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 eines 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. Sie wird durch Referenzzählung mit MemoryBlockAllocation freigegeben. 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

Einige Apps benötigen zusätzliche Daten, 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, hängen 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 als der Block auf die Metadaten zugreift, 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(...);