MemoryBlock HIDL

MemoryBlock HIDL 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 et partagent 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 défaillances de segmentation mmap/munmap et d'espace utilisateur, ce qui améliore les performances. Exemple :

  • L'utilisation de hidl_memory pour chaque allocation de tampon donne en moyenne 238 µs/1 allocation.
  • L'utilisation de MemoryBlock et le partage d'un seul hidl_memory coûtent en moyenne 2,82 us/1 d'allocation.

Architecture

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

MemoryBlock HIDL

Figure 1 : Architecture MemoryBlock HIDL

Utilisation normale

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

Déclarer le HAL

Pour l'exemple IFoo HAL suivant:

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

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

La Android.bp est la suivante:

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

Implémenter le HAL

Pour implémenter l'exemple de HAL:

  1. Obtenez 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 l'hidl_memory acquise:

    #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. Allouer MemoryBlock, qui est une struct 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. Libérez MemoryBlock:

    Return<void> Foo::giveBack(const MemoryBlock& block) {
        memory_dealer->deallocate(block.offset);
    ...
    
  5. Manipuler 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. 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. Examinez le flux pour déterminer si vous devez lockMemory.

    Normalement, MemoryBlock utilise le compte de référence pour gérer le hidl_memory partagé, qui est mmap() la première fois qu'un 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 est mappé tout au long du cycle de vie du verrouillage. 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, le moyen le plus efficace d'utiliser MemoryBlock consiste à allouer/désallouer explicitement. Toutefois, dans les applications complexes, il peut être préférable d'utiliser le compte de références pour le nettoyage. Pour avoir un compteur de références sur MemoryBlock, vous pouvez lier MemoryBlock à un objet de liaison, ce qui permet de compter les références et de désallouer MemoryBlock lorsque le compteur diminue à zéro.

Déclarer le HAL

Lorsque vous déclarez le HAL, décrivez une structure HIDL contenant une instance MemoryBlock et un 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. Il est désallooué par comptage des références avec MemoryBlockAllocation. Exemple :

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

Implémenter le HAL

Exemple d'implémentation côté service de l'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 du HAL:

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

joindre et récupérer des métadonnées ;

Certaines applications ont besoin de données supplémentaires pour se lier à l'MemoryBlock allouée. Vous pouvez ajouter et récupérer des métadonnées de deux manières:

  • Si l'application accède aux métadonnées aussi souvent que le bloc lui-même, ajoutez les métadonnées et transmettez-les toutes dans une struct. 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 fréquemment que le bloc, il est plus efficace de transmettre les métadonnées passivement avec une interface. Exemple :

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

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

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