بلوک حافظه HIDL

HIDL MemoryBlock یک لایه انتزاعی است که بر روی hidl_memory ، HIDL @1.0::IAllocator و HIDL @1.0::IMapper ساخته شده است. این برای سرویس های HIDL طراحی شده است که دارای چندین بلوک حافظه برای به اشتراک گذاشتن یک پشته حافظه واحد هستند.

بهبود عملکرد

استفاده از MemoryBlock در برنامه‌ها می‌تواند به میزان قابل توجهی تعداد mmap / munmap و خطاهای تقسیم‌بندی فضای کاربر را کاهش دهد و در نتیجه عملکرد را بهبود بخشد. مثلا:

  • استفاده از هر hidl_memory برای هر تخصیص بافر به طور متوسط ​​238 us/1 تخصیص است.
  • استفاده از MemoryBlock و به اشتراک گذاری یک hidl_memory به طور میانگین 2.82 us/1 تخصیص دارد.

معماری

معماری HIDL MemoryBlock شامل خدمات HIDL با چندین بلوک حافظه است که یک پشته حافظه را به اشتراک می گذارند:

HIDL MemoryBlock

شکل 1. معماری HIDL MemoryBlock

استفاده معمولی

این بخش نمونه ای از استفاده از MemoryBlock را با اعلام HAL و سپس پیاده سازی HAL ارائه می دهد.

اعلام 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_memory به دست آمده یک HidlMemoryDealer بسازید:

    #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. تخصیص MemoryBlock ، که ساختاری است که با HIDL تعریف شده است.

    نمونه 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. Dealocate 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() -ed است و زمانی که هیچ چیزی به آن اشاره نمی کند munmap() -ed می شود. برای اینکه hidl_memory همیشه نقشه برداری شود، می توانید از lockMemory استفاده کنید، یک شی به سبک RAII که hidl_memory مربوطه را در طول چرخه عمر قفل نگاشت نگه می دارد. مثال:

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

استفاده گسترده

این بخش جزئیاتی در مورد استفاده گسترده از MemoryBlock ارائه می دهد.

استفاده از تعداد مراجع برای مدیریت Memoryblock

در بیشتر مواقع، کارآمدترین راه برای استفاده از MemoryBlock تخصیص/تخصیص صریح است. با این حال، در برنامه های پیچیده استفاده از تعداد مرجع برای جمع آوری زباله ممکن است ایده بهتری باشد. برای داشتن تعداد مرجع در MemoryBlock، می‌توانید MemoryBlock را با یک شی بایندر متصل کنید، که به شمارش مراجع کمک می‌کند و زمانی که شمارش به صفر می‌رسد، MemoryBlock را اختصاص می‌دهد.

اعلام HAL

هنگام اعلام HAL، یک ساختار HIDL را توصیف کنید که حاوی یک MemoryBlock و یک IBase است:

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

    سپس، ابرداده را با MemoryBlock با استفاده از Memory Dealer متصل کنید. مثال:

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