MemoryBlock de HIDL

El MemoryBlock de HIDL es una capa abstracta compilada en 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 en el rendimiento

Usar MemoryBlock en las apps puede reducir significativamente la cantidad de errores de segmentación de mmap/munmap y del espacio del usuario, lo que mejora el rendimiento. Por ejemplo:

  • El uso de hidl_memory por cada asignación de búferes promedia 238 µs/1 asignación.
  • Usar MemoryBlock y compartir un solo hidl_memory promedia 2.82 us/1 asignación.

Arquitectura

La arquitectura de MemoryBlock de HIDL incluye servicios HIDL con varios bloques de memoria que comparten una sola pila de memoria:

MemoryBlock de HIDL

Figura 1: Arquitectura de MemoryBlock de HIDL

Uso normal

En esta sección, se proporciona un ejemplo del uso de MemoryBlock. Primero, se declara el HAL y, luego, se implementa.

Cómo declarar el HAL

En el siguiente ejemplo de HAL de IFoo:

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

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

La Android.bp es la siguiente:

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

Implementa el HAL

Para implementar el HAL de ejemplo, haz lo siguiente:

  1. Obtén el hidl_memory (para obtener detalles, consulta HIDL en 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 una instancia de HidlMemoryDealer con el 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. Asigna MemoryBlock, que es una struct definida con HIDL.

    Ejemplo de MemoryBlock:

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

    Ejemplo en el que se usa 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. Desasigna MemoryBlock:

    Return<void> Foo::giveBack(const MemoryBlock& block) {
        memory_dealer->deallocate(block.offset);
    ...
    
  5. Manipula 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. Configuración de Android.bp:

    shared_libs: [
            "android.hidl.memory@1.0",
    
            "android.hidl.memory.block@1.0"
    
            "android.hidl.memory.token@1.0",
            "libhidlbase",
            "libhidlmemory",
    
  7. Revisa el flujo para determinar si necesitas lockMemory.

    Normalmente, MemoryBlock usa el recuento de referencias para mantener el hidl_memory compartido que se mmap() la primera vez que uno de sus 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` se asigna durante el ciclo de vida del bloqueo. Ejemplo:

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

Uso extendido

En esta sección, se proporcionan detalles sobre el uso extendido de MemoryBlock.

Usa el recuento de referencias para administrar MemoryBlock

En la mayoría de las situaciones, la forma más eficiente de usar MemoryBlock es asignar o liberar memoria de forma explícita. Sin embargo, en apps complicadas, usar el recuento de referencias para la recolección de basura podría ser una mejor idea. Para tener un recuento de referencias en MemoryBlock, puedes vincular MemoryBlock con un objeto vinculador, lo que ayuda a contar las referencias y a liberar MemoryBlock cuando el recuento disminuye a cero.

Cómo declarar el HAL

Cuando declares la HAL, describe un struct de HIDL que contenga una instancia de MemoryBlock y un IBase:

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

struct MemoryBlockAllocation {
    MemoryBlock block;
    IBase refcnt;
};

Usa MemoryBlockAllocation para reemplazar MemoryBlock y quitar el método para devolver MemoryBlock. Se desasigna por recuento de referencias con MemoryBlockAllocation. Ejemplo:

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

Implementa el HAL

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

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 HAL del cliente:

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

Cómo adjuntar y recuperar metadatos

Algunas apps necesitan datos adicionales para vincularse con el MemoryBlock asignado. Puedes agregar y recuperar metadatos con dos métodos:

  • Si la app accede a los metadatos con la misma frecuencia que al bloque en sí, agrega los metadatos y pásalos todos en una struct. Ejemplo:

    import android.hidl.memory.block@1.0::MemoryBlock;
    
    struct MemoryBlockWithMetaData{
        MemoryBlock block;
        MetaDataStruct metaData;
    };
    
  • Si la app accede a los metadatos con mucha menos frecuencia que al 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, vincula los metadatos con MemoryBlock usando MemoryDealer. Ejemplo:

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