HIDL 메모리 블록

HIDL MemoryBlock은 hidl_memory, HIDL @1.0::IAllocatorHIDL @1.0::IMapper에 빌드된 추상적인 레이어입니다. 이는 단일 메모리 힙을 공유하기 위한 메모리 블록이 여러 개 있는 HIDL 서비스용으로 설계되었습니다.

성능 개선

애플리케이션에서 MemoryBlock을 사용하면 mmap/munmap의 개수와 사용자 공간 세분화 오류를 줄일 수 있으므로 성능이 향상됩니다. 예:

  • 각 버퍼 할당에 hidl_memory를 사용하면 할당당 평균 238us가 됩니다.
  • MemoryBlock을 사용하고 단일 hidl_memory를 공유하면 할당당 평균 2.82us가 됩니다.

아키텍처

HIDL MemoryBlock 아키텍처에는 여러 개의 메모리 블록이 단일 메모리 힙을 공유하는 HIDL 서비스가 포함되어 있습니다.

HIDL MemoryBlock

그림 1. HIDL MemoryBlock 아키텍처

일반 사용

이 섹션에서는 먼저 HAL을 선언한 다음 HAL을 구현하여 MemoryBlock을 사용하는 예시를 제공합니다.

HAL 선언

다음은 IFoo HAL 예시입니다.

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

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

Android.bp는 다음과 같습니다.

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

HAL 구현

예시 HAL을 구현하려면 다음 단계를 따르세요.

  1. hidl_memory를 가져옵니다(자세한 내용은 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. 가져온 hidl_memoryHidlMemoryDealer를 만듭니다.

    #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. HIDL로 정의된 구조체인 MemoryBlock을 할당합니다.

    MemoryBlock 예시:

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

    MemoryDealer를 사용하여 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. MemoryBlock 할당 해제:

    Return<void> Foo::giveBack(const MemoryBlock& block) {
        memory_dealer->deallocate(block.offset);
    ...
    
  5. 데이터 조작:

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

    shared_libs: [
            "android.hidl.memory@1.0",
    
            "android.hidl.memory.block@1.0"
    
            "android.hidl.memory.token@1.0",
            "libhidlbase",
            "libhidlmemory",
    
  7. 흐름을 검토하여 lockMemory가 필요한지 결정합니다.

    일반적으로 MemoryBlock은 참조 횟수를 사용하여 공유 hidl_memory를 유지합니다. 이는 MemoryBlock 중 하나가 최초로 매핑될 때 mmap()이 적용되고, 참조 횟수가 없는 경우에는 munmap()이 적용됩니다. hidl_memory를 항상 매핑된 상태로 유지하려면 잠금 수명주기 전체에 걸쳐 상응하는 hidl_memory가 매핑된 상태를 유지하게 해 주는 RAII 스타일 객체인 lockMemory를 사용할 수 있습니다. 예:

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

확장 사용

이 섹션에서는 MemoryBlock의 확장 사용에 관한 세부정보를 제공합니다.

참조 횟수를 사용하여 MemoryBlock 관리

대부분의 경우 MemoryBlock을 가장 효율적으로 사용하는 방법은 명시적으로 할당 및 할당 해제하는 것입니다. 하지만 복잡한 애플리케이션의 경우 가비지 컬렉션에 참조 횟수를 사용하는 것이 더 효과적인 방법일 수 있습니다. MemoryBlock의 참조 횟수를 계산하기 위해 MemoryBlock을 바인더 객체와 결합할 수 있습니다. 이렇게 하면 참조 횟수를 계산할 수 있고, 이 횟수가 0으로 감소하는 경우 MemoryBlock을 할당 해제할 수 있습니다.

HAL 선언

HAL을 선언할 때 MemoryBlock 및 IBase가 포함된 HIDL 구조체를 명시합니다.

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

struct MemoryBlockAllocation {
    MemoryBlock block;
    IBase refcnt;
};

MemoryBlockAllocation을 사용하여 MemoryBlock을 대체하고, 메서드를 삭제하여 MemoryBlock을 돌려줍니다. 이는 MemoryBlockAllocation을 사용한 참조 횟수 계산에 따라 할당 해제됩니다. 예:

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

HAL 구현

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);

HAL의 클라이언트 측 구현 예시:

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

메타데이터 첨부/가져오기

일부 애플리케이션에는 할당된 MemoryBlock과 결합하기 위한 추가 데이터가 필요합니다. 다음 두 가지 방법을 사용하여 메타데이터를 추가하거나 검색할 수 있습니다.

  • 애플리케이션이 블록만큼 자주 메타데이터에 액세스하는 경우 메타데이터를 추가하고 구조체에 모두 전달합니다. 예:

    import android.hidl.memory.block@1.0::MemoryBlock;
    
    struct MemoryBlockWithMetaData{
        MemoryBlock block;
        MetaDataStruct metaData;
    };
    
  • 애플리케이션이 블록보다 메타데이터에 훨씬 더 적게 액세스하는 경우 인터페이스를 통해 메타데이터를 수동으로 전달하는 것이 더 효율적입니다. 예:

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

    그런 다음 Memory Dealer를 사용하여 MemoryBlock과 메타데이터를 결합합니다. 예를 들면 다음과 같습니다.

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