HIDL-Speicherblock

Der 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 über mehrere Speicherblöcke verfügen, um einen einzelnen Speicherheap gemeinsam zu nutzen.

Leistungsverbesserungen

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

  • Die Verwendung von pro hidl_memory für jede Pufferzuweisung beträgt durchschnittlich 238 us/1 Zuweisung.
  • Die Verwendung MemoryBlock und die gemeinsame Nutzung eines einzelnen hidl_memory erfordert durchschnittlich 2,82 us/1 Zuweisung.

Die Architektur

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

HIDL MemoryBlock

Abbildung 1. HIDL MemoryBlock-Architektur

Normaler Gebrauch

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

Deklaration des HAL

Für das folgende Beispiel IFoo HAL:

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

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

Die Android.bp lautet wie folgt:

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

Implementierung des HAL

So implementieren Sie das Beispiel-HAL:

  1. Holen Sie sich hidl_memory (Einzelheiten 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 einen HidlMemoryDealer mit dem erworbenen 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. Allocate MemoryBlock , eine mit HIDL definierte Struktur.

    Beispiel MemoryBlock :

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

    Beispiel für die Verwendung des MemoryDealer zum Zuweisen eines MemoryBlock :

    #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. Bearbeiten Sie die Daten:

    #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. Konfigurieren Sie Android.bp :

    shared_libs: [
            "android.hidl.memory@1.0",
    
            "android.hidl.memory.block@1.0"
    
            "android.hidl.memory.token@1.0",
            "libhidlbase",
            "libhidlmemory",
    
  7. Überprüfen Sie den Ablauf, um festzustellen, ob Sie lockMemory benötigen.

    Normalerweise verwendet der MemoryBlock den Referenzzähler, um den gemeinsam genutzten hidl_memory zu verwalten, der mmap() -ed wird, wenn einer seiner MemoryBlock s zum ersten Mal zugeordnet wird, und munmap() -ed wird, wenn nichts darauf verweist. Um den hidl_memory immer zugeordnet zu halten, können Sie lockMemory verwenden, ein Objekt im RAII-Stil, das den entsprechenden hidl_memory während des gesamten Sperrlebenszyklus zugeordnet hält. Beispiel:

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

Erweiterte Nutzung

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

Verwenden der Referenzanzahl zum Verwalten von Memoryblock

In den meisten Situationen besteht die effizienteste Möglichkeit, MemoryBlock zu verwenden, darin, die Zuordnung explizit zuzuweisen bzw. aufzuheben. In komplizierten Anwendungen ist es jedoch möglicherweise besser, den Referenzzähler für die Speicherbereinigung zu verwenden. Um einen Referenzzähler für MemoryBlock zu haben, können Sie MemoryBlock mit einem Binderobjekt binden, was dabei hilft, die Referenzen zu zählen und den MemoryBlock freizugeben, wenn der Zähler auf Null sinkt.

Deklaration des HAL

Beschreiben Sie beim Deklarieren des HAL eine HIDL-Struktur, die einen MemoryBlock 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 die Methode zu entfernen, um MemoryBlock zurückzugeben. Die Zuordnung wird durch Referenzzählung mit MemoryBlockAllocation aufgehoben. Beispiel:

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

Implementierung des HAL

Beispiel für die serviceseitige 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 des HAL:

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

Anhängen/Abrufen von Metadaten

Einige Anwendungen benötigen zusätzliche Daten, um sich an den zugewiesenen MemoryBlock zu binden. Sie können Metadaten mit zwei Methoden anhängen/abrufen:

  • Wenn die Anwendung genauso 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 Anwendung 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;
    };
    

    Als nächstes binden Sie die Metadaten mithilfe des Memory Dealer an den MemoryBlock. Beispiel:

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