MemoryBlock HIDL

Le HIDL MemoryBlock est une couche abstraite basée sur hidl_memory, HIDL @1.0::IAllocator et HIDL @1.0::IMapper. Il est conçu pour les services HIDL qui comportent plusieurs blocs de mémoire afin de partager un seul tas de mémoire.

Amélioration des performances

L'utilisation de MemoryBlock dans les applications peut réduire considérablement le nombre de mmap/munmap et de défauts de segmentation de l'espace utilisateur, ce qui améliore les performances. Exemple :

  • L'utilisation de hidl_memory par allocation de mémoire tampon représente en moyenne 238 µs/1 allocation.
  • L'utilisation de MemoryBlock et le partage d'un seul hidl_memory représentent en moyenne 2,82 µs/1 allocation.

Architecture

L'architecture HIDL MemoryBlock inclut des services HIDL avec plusieurs blocs de mémoire partageant un seul tas de mémoire :

HIDL MemoryBlock

Figure 1. Architecture HIDL MemoryBlock

Utilisation normale

Cette section fournit un exemple d'utilisation de MemoryBlock en déclarant d'abord la HAL, puis en l'implémentant.

Déclarer la HAL

Pour l'exemple suivant de HAL IFoo :

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

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

Le Android.bp est le suivant :

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

Implémenter la HAL

Pour implémenter l'exemple de HAL :

  1. Obtenez le hidl_memory (pour en savoir plus, consultez 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. Créez une instance HidlMemoryDealer avec le hidl_memory acquis :

    #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. Allouez MemoryBlock, qui est une structure définie avec HIDL.

    Exemple MemoryBlock :

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

    Exemple d'utilisation de MemoryDealer pour allouer 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. Désallouez MemoryBlock :

    Return<void> Foo::giveBack(const MemoryBlock& block) {
        memory_dealer->deallocate(block.offset);
    ...
    
  5. Manipulez les données :

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

    shared_libs: [
            "android.hidl.memory@1.0",
    
            "android.hidl.memory.block@1.0"
    
            "android.hidl.memory.token@1.0",
            "libhidlbase",
            "libhidlmemory",
    
  7. Examinez le flux pour déterminer si vous devez lockMemory.

    Normalement, MemoryBlock utilise le nombre de références pour gérer le partagé hidl_memory qui est mmap()-ed la première fois que l'une de ses 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` mappé tout au long du verrou cycle de vie. Exemple :

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

Utilisation étendue

Cette section fournit des informations sur l'utilisation étendue de MemoryBlock.

Utiliser le nombre de références pour gérer MemoryBlock

Dans la plupart des cas, la manière la plus efficace d'utiliser MemoryBlock consiste à allouer/désallouer explicitement. Toutefois, dans les applications complexes, il peut être préférable d'utiliser le nombre de références pour la récupération de mémoire. Pour que le nombre de références soit sur MemoryBlock, vous pouvez lier MemoryBlock à un objet Binder, ce qui permet de compter les références et de désallouer le MemoryBlock lorsque le nombre diminue à zéro.

Déclarer la HAL

Lorsque vous déclarez la HAL, décrivez une structure HIDL qui contient une instance MemoryBlock et une IBase :

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

struct MemoryBlockAllocation {
    MemoryBlock block;
    IBase refcnt;
};

Utilisez MemoryBlockAllocation pour remplacer MemoryBlock et supprimez la méthode pour renvoyer MemoryBlock. Elle est désallouée par comptage de références avec MemoryBlockAllocation. Exemple :

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

Implémenter la HAL

Exemple d'implémentation côté service 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);

Exemple d'implémentation côté client de la HAL :

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

Associer et récupérer des métadonnées

Certaines applications ont besoin de données supplémentaires pour être liées au MemoryBlock alloué. Vous pouvez ajouter et récupérer des métadonnées à l'aide de deux méthodes :

  • Si l'application accède aux métadonnées aussi souvent qu'au bloc lui-même, ajoutez les métadonnées et transmettez-les toutes dans une structure. Exemple :

    import android.hidl.memory.block@1.0::MemoryBlock;
    
    struct MemoryBlockWithMetaData{
        MemoryBlock block;
        MetaDataStruct metaData;
    };
    
  • Si l'application accède aux métadonnées beaucoup moins souvent qu'au bloc, il est plus efficace de transmettre les métadonnées de manière passive avec une interface. Exemple :

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

    Ensuite, liez les métadonnées au MemoryBlock à l'aide de MemoryDealer. Exemple :

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