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

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

  • El uso de hidl_memory por cada asignación de búfer tiene un promedio de 238 μs por 1 asignación.
  • El uso de MemoryBlock y el uso compartido de un solo hidl_memory tienen un promedio de 2.82 μs/1 asignación.

Arquitectura

La arquitectura MemoryBlock del HIDL incluye servicios HIDL con varios bloques de memoria que comparten un solo montón 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 la HAL

En el siguiente ejemplo de la HAL de IFoo:

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",
        ...
};

Implementa el HAL

Para implementar la HAL de ejemplo, haz lo siguiente:

  1. Obtén el hidl_memory (para obtener más información, consulta 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 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 estructura definida con HIDL.

    Ejemplo de MemoryBlock:

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

    Ejemplo de uso de 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. Libera 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. Config 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.

    Por lo general, MemoryBlock usa el recuento de referencias para mantener el hidl_memory compartido, que se mmap()-ed 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 reasignar de forma explícita. Sin embargo, en apps complicadas que usan 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, puedes vincular MemoryBlock con un objeto Binder, que ayuda a contar las referencias y desasignar MemoryBlock cuando el recuento disminuye a cero.

Cómo declarar la HAL

Cuando declares la HAL, describe una estructura 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 quita el método para devolver MemoryBlock. Se desaloca mediante el 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 lado del cliente del HAL:

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

Adjunta y recupera metadatos

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

  • Si la app accede a los metadatos con la misma frecuencia que al bloque, adjúntalos y pásalos todos en una estructura. 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 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;
    };
    

    Luego, 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(...);