HIDL MemoryBlock

HIDL MemoryBlock là một lớp trừu tượng được xây dựng trên hidl_memory, HIDL @1.0::IAllocatorHIDL @1.0::IMapper. Công cụ này được thiết kế cho các dịch vụ HIDL có nhiều khối bộ nhớ để chia sẻ một vùng nhớ duy nhất.

Cải thiện hiệu suất

Việc sử dụng MemoryBlock trong các ứng dụng có thể giảm đáng kể số lượng lỗi phân đoạn không gian người dùng và mmap/munmap, nhờ đó cải thiện hiệu suất. Ví dụ:

  • Việc sử dụng hidl_memory cho mỗi lần phân bổ bộ nhớ đệm trung bình là 238 us/1 lần phân bổ.
  • Sử dụng MemoryBlock và chia sẻ một hidl_memory trung bình là 2,82 us/1 lần phân bổ.

Kiến trúc

Cấu trúc MemoryBlock HIDL bao gồm các dịch vụ HIDL có nhiều khối bộ nhớ dùng chung một vùng nhớ duy nhất:

HIDL MemoryBlock

Hình 1. Cấu trúc HIDL MemoryBlock

Cách sử dụng thông thường

Phần này cung cấp một ví dụ về cách sử dụng MemoryBlock bằng cách khai báo HAL trước, sau đó triển khai HAL.

Khai báo HAL

Đối với ví dụ sau đây về IFoo HAL:

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

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

Android.bp như sau:

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

Triển khai HAL

Cách triển khai HAL mẫu:

  1. Nhận hidl_memory (để biết thông tin chi tiết, hãy tham khảo 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. Tạo một thực thể HidlMemoryDealer bằng hidl_memory đã thu được:

    #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. Phân bổ MemoryBlock, là một cấu trúc được xác định bằng HIDL.

    Ví dụ MemoryBlock:

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

    Ví dụ về cách sử dụng MemoryDealer để phân bổ 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. Huỷ phân bổ MemoryBlock:

    Return<void> Foo::giveBack(const MemoryBlock& block) {
        memory_dealer->deallocate(block.offset);
    ...
    
  5. Thao tác với dữ liệu:

    #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. Cấu hình Android.bp:

    shared_libs: [
            "android.hidl.memory@1.0",
    
            "android.hidl.memory.block@1.0"
    
            "android.hidl.memory.token@1.0",
            "libhidlbase",
            "libhidlmemory",
    
  7. Xem lại quy trình để xác định xem bạn có cần lockMemory hay không.

    Thông thường, MemoryBlock sử dụng số lượt tham chiếu để duy trì hidl_memory dùng chung. hidl_memory này được mmap() lần đầu tiên một trong các 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 của nó được ánh xạ trong suốt vòng đời khoá. Ví dụ:

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

Sử dụng mở rộng

Phần này cung cấp thông tin chi tiết về phạm vi sử dụng mở rộng của MemoryBlock.

Sử dụng số lượt tham chiếu để quản lý MemoryBlock

Trong hầu hết các trường hợp, cách hiệu quả nhất để sử dụng MemoryBlock là phân bổ/huỷ phân bổ một cách rõ ràng. Tuy nhiên, trong các ứng dụng phức tạp, việc sử dụng số lượt tham chiếu để thu thập rác có thể là một ý tưởng hay hơn. Để có số lượt tham chiếu trên MemoryBlock, bạn có thể liên kết MemoryBlock với một đối tượng liên kết. Đối tượng này giúp đếm số lượt tham chiếu và giải phóng MemoryBlock khi số lượt tham chiếu giảm xuống 0.

Khai báo HAL

Khi khai báo HAL, hãy mô tả một cấu trúc HIDL chứa một thực thể MemoryBlock và một IBase:

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

struct MemoryBlockAllocation {
    MemoryBlock block;
    IBase refcnt;
};

Sử dụng MemoryBlockAllocation để thay thế MemoryBlock và xoá phương thức để trả lại MemoryBlock. Vùng nhớ này được giải phóng bằng cách đếm số lượt tham chiếu bằng MemoryBlockAllocation. Ví dụ:

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

Triển khai HAL

Ví dụ về cách triển khai phía dịch vụ của 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);

Ví dụ về cách triển khai HAL phía máy khách:

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

Đính kèm và truy xuất siêu dữ liệu

Một số ứng dụng cần dữ liệu bổ sung để liên kết với MemoryBlock được phân bổ. Bạn có thể thêm và truy xuất siêu dữ liệu bằng hai phương thức:

  • Nếu ứng dụng truy cập vào siêu dữ liệu thường xuyên như chính khối, hãy thêm siêu dữ liệu và truyền tất cả siêu dữ liệu đó vào một cấu trúc. Ví dụ:

    import android.hidl.memory.block@1.0::MemoryBlock;
    
    struct MemoryBlockWithMetaData{
        MemoryBlock block;
        MetaDataStruct metaData;
    };
    
  • Nếu ứng dụng truy cập vào siêu dữ liệu ít thường xuyên hơn nhiều so với khối, thì việc truyền siêu dữ liệu một cách thụ động bằng một giao diện sẽ hiệu quả hơn. Ví dụ:

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

    Tiếp theo, hãy liên kết siêu dữ liệu với MemoryBlock bằng MemoryDealer. Ví dụ:

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