Blocco memoria HIDL

HIDL MemoryBlock è un livello astratto costruito su hidl_memory , HIDL @1.0::IAllocator e HIDL @1.0::IMapper . È progettato per i servizi HIDL che dispongono di più blocchi di memoria per condividere un singolo heap di memoria.

Miglioramenti delle prestazioni

L'utilizzo di MemoryBlock nelle applicazioni può ridurre significativamente il numero di errori di segmentazione mmap / munmap e dello spazio utente, migliorando così le prestazioni. Per esempio:

  • L'utilizzo di per hidl_memory per ogni allocazione del buffer ha una media di 238 us/1 allocazione.
  • L'utilizzo MemoryBlock e la condivisione di un singolo hidl_memory ha un'allocazione media di 2,82 us/1.

Architettura

L'architettura HIDL MemoryBlock include servizi HIDL con più blocchi di memoria che condividono un singolo heap di memoria:

Blocco memoria HIDL

Figura 1. Architettura HIDL MemoryBlock

Utilizzo normale

Questa sezione fornisce un esempio di utilizzo di MemoryBlock dichiarando prima l'HAL e quindi implementando l'HAL.

Dichiarare l'HAL

Per il seguente esempio IFoo HAL:

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

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

Il Android.bp è il seguente:

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

Implementazione dell'HAL

Per implementare l'HAL di esempio:

  1. Ottieni hidl_memory (per i dettagli, fai riferimento a 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. Crea un HidlMemoryDealer con l' hidl_memory acquisito:

    #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. Allocare MemoryBlock , che è una struttura definita con HIDL.

    Esempio MemoryBlock :

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

    Esempio di utilizzo di MemoryDealer per allocare un 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. Deallocare MemoryBlock :

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

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

    shared_libs: [
            "android.hidl.memory@1.0",
    
            "android.hidl.memory.block@1.0"
    
            "android.hidl.memory.token@1.0",
            "libhidlbase",
            "libhidlmemory",
    
  7. Esaminare il flusso per determinare se è necessario lockMemory .

    Normalmente, MemoryBlock utilizza il conteggio dei riferimenti per mantenere la hidl_memory condivisa che viene mmap() la prima volta che uno dei suoi MemoryBlock viene mappato e viene munmap() quando nulla si riferisce ad esso. Per mantenere hidl_memory sempre mappato, puoi utilizzare lockMemory , un oggetto in stile RAII che mantiene il corrispondente hidl_memory mappato per tutto il ciclo di vita del blocco. Esempio:

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

Utilizzo prolungato

Questa sezione fornisce dettagli sull'utilizzo esteso di MemoryBlock .

Utilizzo del conteggio dei riferimenti per gestire Memoryblock

Nella maggior parte delle situazioni, il modo più efficiente per utilizzare MemoryBlock è allocare/deallocare in modo esplicito. Tuttavia, in applicazioni complesse, l'utilizzo del conteggio dei riferimenti per la garbage collection potrebbe essere un'idea migliore. Per avere il conteggio dei riferimenti su MemoryBlock, puoi associare MemoryBlock a un oggetto raccoglitore, che aiuta a contare i riferimenti e deallocare MemoryBlock quando il conteggio diminuisce a zero.

Dichiarare l'HAL

Quando dichiari l'HAL, descrivi una struttura HIDL che contiene un MemoryBlock e un IBase:

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

struct MemoryBlockAllocation {
    MemoryBlock block;
    IBase refcnt;
};

Utilizzare MemoryBlockAllocation per sostituire MemoryBlock e rimuovere il metodo per restituire MemoryBlock . Verrà deallocato tramite il conteggio dei riferimenti con MemoryBlockAllocation . Esempio:

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

Implementazione dell'HAL

Esempio dell'implementazione lato servizio dell'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);

Esempio dell'implementazione lato client dell'HAL:

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

Allegare/recuperare metadati

Alcune applicazioni necessitano di dati aggiuntivi da associare al MemoryBlock allocato. Puoi aggiungere/recuperare metadati utilizzando due metodi:

  • Se l'applicazione accede ai metadati con la stessa frequenza del blocco stesso, aggiungi i metadati e passali tutti in una struttura. Esempio:

    import android.hidl.memory.block@1.0::MemoryBlock;
    
    struct MemoryBlockWithMetaData{
        MemoryBlock block;
        MetaDataStruct metaData;
    };
    
  • Se l'applicazione accede ai metadati molto meno frequentemente rispetto al blocco, è più efficiente passare passivamente i metadati con un'interfaccia. Esempio:

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

    Successivamente, associa i metadati con MemoryBlock utilizzando Memory Dealer. Esempio:

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