Bloque de memoria HIDL

HIDL MemoryBlock es una capa abstracta construida sobre hidl_memory , HIDL @1.0::IAllocator y HIDL @1.0::IMapper . Está diseñado para servicios HIDL que tienen varios bloques de memoria para compartir un solo montón de memoria.

Mejoras de rendimiento

El uso de MemoryBlock en las aplicaciones puede reducir significativamente la cantidad de fallas de segmentación del espacio de usuario y mmap / munmap , mejorando así el rendimiento. Por ejemplo:

  • El uso por hidl_memory para cada asignación de búfer promedia 238 us/1 de asignación.
  • Usar MemoryBlock y compartir una sola hidl_memory promedia 2,82 us/1 de asignación.

Arquitectura

La arquitectura HIDL MemoryBlock incluye servicios HIDL con varios bloques de memoria que comparten un solo montón de memoria:

Bloque de memoria HIDL

Figura 1. Arquitectura HIDL MemoryBlock

uso normal

Esta sección proporciona un ejemplo del uso de MemoryBlock declarando primero la HAL y luego implementando la HAL.

Declaración de la HAL

Para el siguiente ejemplo IFoo HAL:

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

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

El Android.bp es el siguiente:

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

Implementando el HAL

Para implementar el HAL de ejemplo:

  1. Obtenga hidl_memory (para obtener detalles, 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. Haz un HidlMemoryDealer con la hidl_memory adquirida:

    #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. Asignar MemoryBlock , que es una estructura definida con HIDL.

    Ejemplo MemoryBlock :

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

    Ejemplo usando el MemoryDealer para asignar 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. MemoryBlock :

    Return<void> Foo::giveBack(const MemoryBlock& block) {
        memory_dealer->deallocate(block.offset);
    ...
    
  5. Manipular los datos:

    #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 el flujo para determinar si necesita lockMemory .

    Normalmente, el MemoryBlock usa el conteo de referencias para mantener la hidl_memory compartida, que es mmap() -ed la primera vez que se mapea uno de sus MemoryBlock s y es munmap() -ed cuando nada se refiere a él. Para mantener la hidl_memory siempre asignada, puede usar lockMemory , un objeto de estilo RAII que mantiene la hidl_memory correspondiente asignada durante todo el ciclo de vida de la cerradura. Ejemplo:

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

Uso extendido

Esta sección proporciona detalles sobre el uso extendido de MemoryBlock .

Uso del recuento de referencias para administrar Memoryblock

En la mayoría de las situaciones, la forma más eficiente de usar MemoryBlock es asignar/desasignar explícitamente. Sin embargo, en aplicaciones complicadas, usar el recuento de referencias para la recolección de elementos no utilizados podría ser una mejor idea. Para tener un recuento de referencias en MemoryBlock, puede vincular MemoryBlock con un objeto de vinculación, lo que ayuda a contar las referencias y desasignar MemoryBlock cuando el recuento disminuye a cero.

Declaración de la HAL

Al declarar la HAL, describa una estructura HIDL que contenga un MemoryBlock y una IBase:

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

struct MemoryBlockAllocation {
    MemoryBlock block;
    IBase refcnt;
};

Use MemoryBlockAllocation para reemplazar MemoryBlock y elimine el método para devolver MemoryBlock . Se desasignará por referencia contando con MemoryBlockAllocation . Ejemplo:

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

Implementando el HAL

Ejemplo de la implementación del lado del servicio de la 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);

Ejemplo de la implementación del lado del cliente de la HAL:

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

Adjuntar/recuperar metadatos

Algunas aplicaciones necesitan datos adicionales para enlazar con el MemoryBlock asignado. Puede agregar/recuperar metadatos usando dos métodos:

  • Si la aplicación accede a los metadatos con tanta frecuencia como el propio bloque, agregue los metadatos y páselos todos en una estructura. Ejemplo:

    import android.hidl.memory.block@1.0::MemoryBlock;
    
    struct MemoryBlockWithMetaData{
        MemoryBlock block;
        MetaDataStruct metaData;
    };
    
  • Si la aplicación accede a los metadatos con mucha menos frecuencia que el bloque, es más eficiente pasar los metadatos de forma pasiva con una interfaz. Ejemplo:

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

    A continuación, vincule los metadatos con el MemoryBlock utilizando el Memory Dealer. Ejemplo:

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