Bloco de Memória HIDL

O HIDL MemoryBlock é uma camada abstrata construída em hidl_memory , HIDL @1.0::IAllocator e HIDL @1.0::IMapper . Ele é projetado para serviços HIDL que possuem vários blocos de memória para compartilhar um único heap de memória.

Melhorias de desempenho

O uso de MemoryBlock em aplicativos pode reduzir significativamente o número de falhas de segmentação de mmap / munmap e de espaço do usuário, melhorando assim o desempenho. Por exemplo:

  • Usar per hidl_memory para cada alocação de buffer resulta em média de 238 us/1 alocação.
  • Usando MemoryBlock e compartilhando uma única hidl_memory média de alocação é de 2,82 us/1.

Arquitetura

A arquitetura HIDL MemoryBlock inclui serviços HIDL com vários blocos de memória compartilhando um único heap de memória:

Bloco de Memória HIDL

Figura 1. Arquitetura HIDL MemoryBlock

Uso normal

Esta seção fornece um exemplo de uso do MemoryBlock declarando primeiro o HAL e implementando o HAL.

Declarando o HAL

Para o seguinte exemplo IFoo HAL:

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

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

O Android.bp é o seguinte:

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

Implementação do HAL

Para implementar o exemplo HAL:

  1. Obtenha o hidl_memory (para obter detalhes, consulte 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. Faça um HidlMemoryDealer com o hidl_memory adquirido:

    #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. Alocar MemoryBlock , que é uma estrutura definida com HIDL.

    Exemplo MemoryBlock :

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

    Exemplo usando o MemoryDealer para alocar um 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 :

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

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

    shared_libs: [
            "android.hidl.memory@1.0",
    
            "android.hidl.memory.block@1.0"
    
            "android.hidl.memory.token@1.0",
            "libhidlbase",
            "libhidlmemory",
    
  7. Revise o fluxo para determinar se você precisa lockMemory .

    Normalmente, o MemoryBlock usa a contagem de referência para manter o hidl_memory compartilhado que é mmap() -ed na primeira vez que um de seus MemoryBlock s é mapeado e é munmap() -ed quando nada se refere a ele. Para manter o hidl_memory sempre mapeado, você pode usar lockMemory , um objeto estilo RAII que mantém o hidl_memory correspondente mapeado durante todo o ciclo de vida do bloqueio. Exemplo:

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

Uso estendido

Esta seção fornece detalhes sobre o uso estendido de MemoryBlock .

Usando contagem de referência para gerenciar Memoryblock

Na maioria das situações, a maneira mais eficiente de usar MemoryBlock é alocar/desalocar explicitamente. No entanto, em aplicativos complicados, usar contagem de referência para coleta de lixo pode ser uma ideia melhor. Para ter uma contagem de referência em MemoryBlock, você pode vincular MemoryBlock a um objeto binder, que ajuda a contar as referências e desalocar o MemoryBlock quando a contagem diminui para zero.

Declarando o HAL

Ao declarar o HAL, descreva uma estrutura HIDL que contém um MemoryBlock e um IBase:

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

struct MemoryBlockAllocation {
    MemoryBlock block;
    IBase refcnt;
};

Use o MemoryBlockAllocation para substituir MemoryBlock e remova o método para devolver o MemoryBlock . Ele será desalocado por contagem de referência com MemoryBlockAllocation . Exemplo:

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

Implementação do HAL

Exemplo da implementação do lado do serviço do 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);

Exemplo da implementação do lado do cliente do HAL:

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

Anexar/recuperar metadados

Alguns aplicativos precisam de dados adicionais para vincular com o MemoryBlock alocado. Você pode anexar/recuperar metadados usando dois métodos:

  • Se o aplicativo acessar os metadados com a mesma frequência que o próprio bloco, anexe os metadados e passe-os todos em um struct. Exemplo:

    import android.hidl.memory.block@1.0::MemoryBlock;
    
    struct MemoryBlockWithMetaData{
        MemoryBlock block;
        MetaDataStruct metaData;
    };
    
  • Se o aplicativo acessa os metadados com muito menos frequência do que o bloco, é mais eficiente passar os metadados passivamente com uma interface. Exemplo:

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

    Em seguida, vincule os metadados ao MemoryBlock usando o Memory Dealer. Exemplo:

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