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 múltiples bloques de memoria para compartir un único montón de memoria.

Mejoras de rendimiento

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

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

Arquitectura

La arquitectura HIDL MemoryBlock incluye servicios HIDL con múltiples bloques de memoria que comparten un único 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 HAL y luego implementando HAL.

Declarando el 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 ejemplo HAL:

  1. Obtenga hidl_memory (para más 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. Cree 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. Asigne MemoryBlock , que es una estructura definida con HIDL.

    Ejemplo 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. Desasignar 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, MemoryBlock utiliza el recuento de referencias para mantener la hidl_memory compartida que tiene mmap() la primera vez que se asigna uno de sus MemoryBlock y munmap() cuando nada hace referencia a él. Para mantener hidl_memory siempre mapeado, puede usar lockMemory , un objeto de estilo RAII que mantiene el hidl_memory correspondiente mapeado durante todo el ciclo de vida del bloqueo. 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 gestionar Memoryblock

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

Declarando el HAL

Al declarar 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;
};

Utilice 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 de servicio de 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 HAL:

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

Adjuntar/recuperar metadatos

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

  • Si la aplicación accede a los metadatos con tanta frecuencia como al bloque mismo, 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 MemoryBlock utilizando Memory Dealer. Ejemplo:

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