Bộ nhớ HIDL

MemoryBlock HIDL 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. Lớp 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ớ khối xếp.

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

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

  • Việc sử dụng mỗi hidl_memory cho mỗi lượt phân bổ bộ đệm trung bình là 238 us/1 lượt phân bổ.
  • Việc sử dụng MemoryBlock và chia sẻ một hidl_memory trung bình là 2,82 us/1 lượt 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ớ chia sẻ một vùng nhớ khối xếp:

HIDL MemoryBlock

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

Mức sử dụng thông thường

Phần này đưa ra 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. Tải 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 nạp:

    #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 (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. Giải phóng 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 được mmap() trong 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 ánh xạ trong suốt vòng đời khoá. Ví dụ:

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

Mở rộng phạm vi sử dụng

Phần này cung cấp thông tin chi tiết về việc sử dụng mở rộng 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ổ/giải phóng một cách rõ ràng. Tuy nhiên, trong các ứng dụng phức tạp, bạn nên sử dụng số lượt tham chiếu để thu gom rác. Để 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ều này giúp đếm các lượt tham chiếu và giải phóng MemoryBlock khi số lượt tham chiếu giảm xuống còn 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 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 HAL phía dịch vụ:

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 thêm dữ liệu để liên kết với MemoryBlock được phân bổ. Bạn có thể nối và truy xuất siêu dữ liệu bằng hai phương thức:

  • Nếu ứng dụng truy cập 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 đó trong 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 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(...);