คิวข้อความด่วน (FMQ)

หากคุณกำลังมองหาการสนับสนุน AIDL โปรดดู FMQ พร้อม AIDL ด้วย

โครงสร้างพื้นฐานการเรียกขั้นตอนระยะไกล (RPC) ของ HIDL ใช้กลไก Binder ซึ่งหมายความว่าการเรียกเกี่ยวข้องกับค่าใช้จ่าย จำเป็นต้องมีการดำเนินการเคอร์เนล และอาจทริกเกอร์การดำเนินการของตัวกำหนดตารางเวลา อย่างไรก็ตาม ในกรณีที่ต้องถ่ายโอนข้อมูลระหว่างกระบวนการที่มีค่าใช้จ่ายน้อยกว่าและไม่เกี่ยวข้องกับเคอร์เนล ระบบจะใช้ระบบ Fast Message Queue (FMQ)

FMQ สร้างคิวข้อความด้วยคุณสมบัติที่ต้องการ อ็อบเจ็กต์ MQDescriptorSync หรือ MQDescriptorUnsync สามารถส่งผ่านการเรียก HIDL RPC และใช้โดยกระบวนการรับเพื่อเข้าถึงคิวข้อความ

Fast Message Queues รองรับเฉพาะใน C++ และบนอุปกรณ์ที่ใช้ Android 8.0 ขึ้นไป

ประเภทคิวข้อความ

Android รองรับคิวสองประเภท (เรียกว่า รสชาติ ):

  • คิว ที่ไม่ซิง โครไนซ์ได้รับอนุญาตให้ล้น และสามารถมีผู้อ่านได้จำนวนมาก ผู้อ่านแต่ละคนจะต้องอ่านข้อมูลทันเวลาหรือสูญหาย
  • คิว ที่ซิงโครไนซ์ ไม่ได้รับอนุญาตให้ล้น และสามารถมีเครื่องอ่านได้เพียงเครื่องเดียวเท่านั้น

คิวทั้งสองประเภทไม่ได้รับอนุญาตให้อันเดอร์โฟลว์ (การอ่านจากคิวว่างจะล้มเหลว) และสามารถมีตัวเขียนได้เพียงคนเดียวเท่านั้น

ไม่ซิงโครไนซ์

คิวที่ไม่ซิงโครไนซ์จะมีตัวเขียนเพียงคนเดียว แต่สามารถมีตัวอ่านจำนวนเท่าใดก็ได้ มีตำแหน่งการเขียนหนึ่งตำแหน่งสำหรับคิว อย่างไรก็ตาม ผู้อ่านแต่ละคนจะติดตามตำแหน่งการอ่านของตนเองอย่างเป็นอิสระ

การเขียนลงในคิวจะสำเร็จเสมอ (ไม่ได้ตรวจสอบโอเวอร์โฟลว์) ตราบใดที่ไม่ใหญ่กว่าความจุของคิวที่กำหนดค่าไว้ (การเขียนที่ใหญ่กว่าความจุของคิวจะล้มเหลวในทันที) เนื่องจากผู้อ่านแต่ละคนอาจมีตำแหน่งการอ่านที่แตกต่างกัน แทนที่จะรอให้ผู้อ่านทุกคนอ่านข้อมูลทุกชิ้น ข้อมูลจึงหลุดออกจากคิวเมื่อใดก็ตามที่การเขียนใหม่ต้องการพื้นที่

ผู้อ่านมีหน้าที่ดึงข้อมูลก่อนที่จะหลุดออกจากจุดสิ้นสุดของคิว การอ่านที่พยายามอ่านข้อมูลมากกว่าที่มีอยู่จะล้มเหลวทันที (หากไม่ได้บล็อก) หรือรอให้มีข้อมูลเพียงพอ (หากบล็อก) การอ่านที่พยายามอ่านข้อมูลมากกว่าความจุของคิวมักจะล้มเหลวในทันที

หากผู้อ่านไม่สามารถติดตามผู้เขียนได้ ดังนั้นปริมาณข้อมูลที่เขียนและยังไม่ได้อ่านโดยผู้อ่านนั้นมีมากกว่าความจุของคิว การอ่านครั้งถัดไปจะไม่ส่งคืนข้อมูล แต่จะรีเซ็ตตำแหน่งการอ่านของผู้อ่านให้เท่ากับตำแหน่งการเขียนล่าสุด จากนั้นจะส่งคืนความล้มเหลว หากข้อมูลที่พร้อมให้อ่านได้รับการตรวจสอบหลังจากการโอเวอร์โฟลว์ แต่ก่อนที่จะอ่านครั้งถัดไป จะแสดงข้อมูลที่พร้อมให้อ่านมากกว่าความจุของคิว ซึ่งบ่งชี้ว่าเกิดการโอเวอร์โฟลว์แล้ว (หากคิวล้นระหว่างการตรวจสอบข้อมูลที่มีอยู่กับการพยายามอ่านข้อมูลนั้น สิ่งเดียวที่บ่งชี้ถึงโอเวอร์โฟลว์คือการอ่านล้มเหลว)

ผู้อ่านคิวที่ไม่ซิงโครไนซ์อาจไม่ต้องการรีเซ็ตตัวชี้การอ่านและเขียนของคิว ดังนั้น เมื่อสร้างคิวจากตัวอธิบาย ผู้อ่านควรใช้อาร์กิวเมนต์ 'false' สำหรับพารามิเตอร์ 'resetPointers'

ซิงโครไนซ์

คิวที่ซิงโครไนซ์มีตัวเขียนหนึ่งตัวและเครื่องอ่านหนึ่งตัวที่มีตำแหน่งการเขียนเดียวและตำแหน่งการอ่านเดียว เป็นไปไม่ได้ที่จะเขียนข้อมูลมากกว่าที่คิวมีพื้นที่ว่างหรืออ่านข้อมูลมากกว่าที่คิวมีอยู่ในปัจจุบัน ขึ้นอยู่กับว่ามีการเรียกใช้ฟังก์ชันการเขียนหรืออ่านการบล็อกหรือไม่บล็อก ความพยายามที่จะเกินพื้นที่ว่างหรือข้อมูลอาจส่งคืนความล้มเหลวทันทีหรือบล็อกจนกว่าการดำเนินการที่ต้องการจะเสร็จสมบูรณ์ ความพยายามที่จะอ่านหรือเขียนข้อมูลมากกว่าความจุของคิวจะล้มเหลวทันทีเสมอ

การตั้งค่า FMQ

คิวข้อความต้องมีออบเจ็กต์ MessageQueue หลายรายการ: หนึ่งรายการสำหรับเขียน และอย่างน้อยหนึ่งรายการสำหรับอ่าน ไม่มีการกำหนดค่าที่ชัดเจนว่าจะใช้วัตถุใดในการเขียนหรืออ่าน มันขึ้นอยู่กับผู้ใช้เพื่อให้แน่ใจว่าไม่มีการใช้อ็อบเจ็กต์สำหรับทั้งการอ่านและการเขียน มีผู้เขียนมากที่สุดหนึ่งคน และสำหรับคิวที่ซิงโครไนซ์ มีผู้อ่านมากที่สุดหนึ่งคน

การสร้างวัตถุ MessageQueue แรก

คิวข้อความถูกสร้างขึ้นและกำหนดค่าด้วยการโทรเพียงครั้งเดียว:

#include <fmq/MessageQueue.h>
using android::hardware::kSynchronizedReadWrite;
using android::hardware::kUnsynchronizedWrite;
using android::hardware::MQDescriptorSync;
using android::hardware::MQDescriptorUnsync;
using android::hardware::MessageQueue;
....
// For a synchronized non-blocking FMQ
mFmqSynchronized =
  new (std::nothrow) MessageQueue<uint16_t, kSynchronizedReadWrite>
      (kNumElementsInQueue);
// For an unsynchronized FMQ that supports blocking
mFmqUnsynchronizedBlocking =
  new (std::nothrow) MessageQueue<uint16_t, kUnsynchronizedWrite>
      (kNumElementsInQueue, true /* enable blocking operations */);
  • ตัวเริ่มต้น MessageQueue<T, flavor>(numElements) จะสร้างและเตรียมใช้งานออบเจ็กต์ที่รองรับฟังก์ชันคิวข้อความ
  • ตัวเริ่มต้น MessageQueue<T, flavor>(numElements, configureEventFlagWord) จะสร้างและเตรียมใช้งานอ็อบเจ็กต์ที่รองรับฟังก์ชันคิวข้อความด้วยการบล็อก
  • flavor สามารถเป็น kSynchronizedReadWrite สำหรับคิวที่ซิงโครไนซ์ หรือ kUnsynchronizedWrite สำหรับคิวที่ไม่ซิงโครไนซ์
  • uint16_t (ในตัวอย่างนี้) อาจเป็น ประเภทที่กำหนด HIDL ใดๆ ที่ไม่เกี่ยวข้องกับบัฟเฟอร์ที่ซ้อนกัน (ไม่มีประเภท string หรือ vec ) ตัวจัดการ หรืออินเทอร์เฟซ
  • kNumElementsInQueue ระบุขนาดของคิวตามจำนวนรายการ จะกำหนดขนาดของบัฟเฟอร์หน่วยความจำที่ใช้ร่วมกันที่จะถูกจัดสรรสำหรับคิว

การสร้างวัตถุ MessageQueue ที่สอง

ด้านที่สองของคิวข้อความถูกสร้างขึ้นโดยใช้อ็อบเจ็กต์ MQDescriptor ที่ได้รับจากด้านแรก ออบเจ็กต์ MQDescriptor ถูกส่งผ่านการเรียก HIDL หรือ AIDL RPC ไปยังกระบวนการที่จะเก็บปลายที่สองของคิวข้อความ MQDescriptor มีข้อมูลเกี่ยวกับคิว รวมถึง:

  • ข้อมูลสำหรับแมปบัฟเฟอร์และเขียนตัวชี้
  • ข้อมูลสำหรับแมปตัวชี้การอ่าน (หากคิวซิงโครไนซ์)
  • ข้อมูลสำหรับแมปคำแฟล็กเหตุการณ์ (หากคิวถูกบล็อก)
  • ประเภทอ็อบเจ็กต์ ( <T, flavor> ) ซึ่งรวมถึง ประเภทองค์ประกอบคิวที่กำหนดโดย HIDL และรสชาติของคิว (ซิงโครไนซ์หรือไม่ซิงโครไนซ์)

วัตถุ MQDescriptor สามารถใช้เพื่อสร้างวัตถุ MessageQueue :

MessageQueue<T, flavor>::MessageQueue(const MQDescriptor<T, flavor>& Desc, bool resetPointers)

พารามิเตอร์ resetPointers ระบุว่าจะรีเซ็ตตำแหน่งการอ่านและเขียนเป็น 0 ในขณะที่สร้างวัตถุ MessageQueue นี้หรือไม่ ในคิวที่ไม่ซิงโครไนซ์ ตำแหน่งการอ่าน (ซึ่งอยู่ในเครื่องของแต่ละออบเจ็กต์ MessageQueue ในคิวที่ไม่ซิงโครไนซ์) จะถูกตั้งค่าเป็น 0 เสมอในระหว่างการสร้าง โดยทั่วไปแล้ว MQDescriptor จะเริ่มต้นได้ในระหว่างการสร้างออบเจ็กต์คิวข้อความแรก สำหรับการควบคุมเพิ่มเติมเหนือหน่วยความจำแบบแบ่งใช้ คุณสามารถตั้งค่า MQDescriptor ด้วยตนเอง ( MQDescriptor ถูกกำหนดไว้ใน system/libhidl/base/include/hidl/MQDescriptor.h ) จากนั้นสร้างทุกอ็อบเจ็กต์ MessageQueue ตามที่อธิบายไว้ในส่วนนี้

การบล็อกคิวและแฟล็กเหตุการณ์

ตามค่าเริ่มต้น คิวไม่รองรับการบล็อกการอ่าน/เขียน การบล็อกการโทรอ่าน/เขียนมีสองประเภท:

  • รูปแบบสั้น โดยมีพารามิเตอร์ 3 ตัว (ตัวชี้ข้อมูล จำนวนรายการ การหมดเวลา) รองรับการบล็อกการดำเนินการอ่าน/เขียนแต่ละรายการในคิวเดียว เมื่อใช้แบบฟอร์มนี้ คิวจะจัดการแฟล็กเหตุการณ์และบิตมาสก์ภายใน และ ออบเจ็กต์คิวข้อความแรก จะต้องเริ่มต้นด้วยพารามิเตอร์ตัวที่สองเป็น true ตัวอย่างเช่น:
    // For an unsynchronized FMQ that supports blocking
    mFmqUnsynchronizedBlocking =
      new (std::nothrow) MessageQueue<uint16_t, kUnsynchronizedWrite>
          (kNumElementsInQueue, true /* enable blocking operations */);
    
  • รูปแบบยาว โดยมีพารามิเตอร์ 6 ตัว (รวมถึงแฟล็กเหตุการณ์และบิตมาสก์) รองรับการใช้ออบเจ็กต์ EventFlag ที่ใช้ร่วมกันระหว่างหลายคิว และอนุญาตให้ระบุบิตมาสก์การแจ้งเตือนที่จะใช้ ในกรณีนี้ จะต้องระบุแฟล็กเหตุการณ์และบิตมาสก์ให้กับการเรียกอ่านและเขียนแต่ละครั้ง

สำหรับรูปแบบยาว EventFlag สามารถระบุได้อย่างชัดเจนในการเรียก readBlocking() และ writeBlocking() แต่ละครั้ง หนึ่งในคิวอาจเริ่มต้นได้ด้วยแฟล็กเหตุการณ์ภายใน ซึ่งจะต้องแยกออกจากออบเจ็กต์ MessageQueue ของคิวนั้นโดยใช้ getEventFlagWord() และใช้เพื่อสร้างออบเจ็กต์ EventFlag ในแต่ละกระบวนการเพื่อใช้กับ FMQ อื่น ๆ อีกทางหนึ่ง ออบเจ็กต์ EventFlag สามารถเตรียมใช้งานด้วยหน่วยความจำที่ใช้ร่วมกันที่เหมาะสมได้

โดยทั่วไป แต่ละคิวควรใช้การบล็อกแบบไม่บล็อก การบล็อกแบบสั้น หรือการบล็อกแบบยาวเพียงรายการเดียวเท่านั้น การผสมสิ่งเหล่านี้ไม่ใช่เรื่องผิดพลาด แต่จำเป็นต้องตั้งโปรแกรมอย่างระมัดระวังเพื่อให้ได้ผลลัพธ์ตามที่ต้องการ

กำลังทำเครื่องหมายหน่วยความจำว่าอ่านอย่างเดียว

ตามค่าเริ่มต้น หน่วยความจำที่ใช้ร่วมกันมีสิทธิ์ในการอ่านและเขียน สำหรับคิวที่ไม่ซิงโครไนซ์ ( kUnsynchronizedWrite ) ผู้เขียนอาจต้องการลบสิทธิ์การเขียนสำหรับผู้อ่านทั้งหมดก่อนที่จะแจกอ็อบเจ็กต์ MQDescriptorUnsync เพื่อให้แน่ใจว่ากระบวนการอื่นๆ ไม่สามารถเขียนลงในคิวได้ ซึ่งแนะนำเพื่อป้องกันจุดบกพร่องหรือพฤติกรรมที่ไม่ดีในกระบวนการของผู้อ่าน หากผู้เขียนต้องการให้ผู้อ่านสามารถรีเซ็ตคิวเมื่อใดก็ตามที่พวกเขาใช้ MQDescriptorUnsync เพื่อสร้างด้านการอ่านของคิว หน่วยความจำจะไม่สามารถทำเครื่องหมายเป็นแบบอ่านอย่างเดียวได้ นี่เป็นพฤติกรรมเริ่มต้นของตัวสร้าง `MessageQueue` ดังนั้น หากมีผู้ใช้คิวนี้อยู่แล้ว จะต้องเปลี่ยนรหัสเพื่อสร้างคิวด้วย resetPointer=false

  • ผู้เขียน: เรียก ashmem_set_prot_region ด้วยตัวอธิบายไฟล์ MQDescriptor และขอบเขตที่ตั้งค่าเป็นแบบอ่านอย่างเดียว ( PROT_READ ):
    int res = ashmem_set_prot_region(mqDesc->handle->data[0], PROT_READ)
  • Reader: สร้างคิวข้อความด้วย resetPointer=false (ค่าเริ่มต้นคือ true ):
    mFmq = new (std::nothrow) MessageQueue(mqDesc, false);

การใช้ MessageQueue

API สาธารณะของวัตถุ MessageQueue คือ:

size_t availableToWrite()  // Space available (number of elements).
size_t availableToRead()  // Number of elements available.
size_t getQuantumSize()  // Size of type T in bytes.
size_t getQuantumCount() // Number of items of type T that fit in the FMQ.
bool isValid() // Whether the FMQ is configured correctly.
const MQDescriptor<T, flavor>* getDesc()  // Return info to send to other process.

bool write(const T* data)  // Write one T to FMQ; true if successful.
bool write(const T* data, size_t count) // Write count T's; no partial writes.

bool read(T* data);  // read one T from FMQ; true if successful.
bool read(T* data, size_t count);  // Read count T's; no partial reads.

bool writeBlocking(const T* data, size_t count, int64_t timeOutNanos = 0);
bool readBlocking(T* data, size_t count, int64_t timeOutNanos = 0);

// Allows multiple queues to share a single event flag word
std::atomic<uint32_t>* getEventFlagWord();

bool writeBlocking(const T* data, size_t count, uint32_t readNotification,
uint32_t writeNotification, int64_t timeOutNanos = 0,
android::hardware::EventFlag* evFlag = nullptr); // Blocking write operation for count Ts.

bool readBlocking(T* data, size_t count, uint32_t readNotification,
uint32_t writeNotification, int64_t timeOutNanos = 0,
android::hardware::EventFlag* evFlag = nullptr) // Blocking read operation for count Ts;

//APIs to allow zero copy read/write operations
bool beginWrite(size_t nMessages, MemTransaction* memTx) const;
bool commitWrite(size_t nMessages);
bool beginRead(size_t nMessages, MemTransaction* memTx) const;
bool commitRead(size_t nMessages);

availableToWrite() และ availableToRead() สามารถใช้เพื่อกำหนดจำนวนข้อมูลที่สามารถถ่ายโอนได้ในการดำเนินการครั้งเดียว ในคิวที่ไม่ซิงโครไนซ์:

  • availableToWrite() จะส่งคืนความจุของคิวเสมอ
  • ผู้อ่านแต่ละคนมีตำแหน่งการอ่านของตัวเองและทำการคำนวณของตัวเองสำหรับ availableToRead()
  • จากมุมมองของผู้อ่านที่ช้า คิวจะได้รับอนุญาตให้ล้น สิ่งนี้อาจส่งผลให้ availableToRead() ส่งกลับค่าที่ใหญ่กว่าขนาดของคิว การอ่านครั้งแรกหลังจากการโอเวอร์โฟลว์จะล้มเหลว และส่งผลให้ตำแหน่งการอ่านสำหรับตัวอ่านนั้นถูกตั้งค่าเท่ากับตัวชี้การเขียนปัจจุบัน ไม่ว่าโอเวอร์โฟลว์จะถูกรายงานผ่าน availableToRead() หรือไม่ก็ตาม

เมธอด read() และ write() จะคืนค่า true หากข้อมูลที่ร้องขอทั้งหมดสามารถ (และถูก) ถ่ายโอนไปยัง/จากคิวได้ วิธีการเหล่านี้ไม่ได้ปิดกั้น พวกเขาประสบความสำเร็จ (และส่งคืน true ) หรือส่งคืนความล้มเหลว ( false ) ทันที

เมธอด readBlocking() และ writeBlocking() รอจนกว่าการดำเนินการที่ร้องขอจะเสร็จสมบูรณ์ หรือจนกว่าจะหมดเวลา (ค่า timeOutNanos เป็น 0 หมายถึงไม่หมดเวลา)

การดำเนินการบล็อกจะดำเนินการโดยใช้คำแฟล็กเหตุการณ์ ตามค่าเริ่มต้น แต่ละคิวจะสร้างและใช้คำแฟล็กของตัวเองเพื่อรองรับรูปแบบสั้นของ readBlocking() และ writeBlocking() เป็นไปได้ที่หลายคิวจะใช้คำเดียวร่วมกัน เพื่อให้กระบวนการสามารถรอการเขียนหรืออ่านคิวใดๆ ได้ ตัวชี้ไปยังคำแฟล็กเหตุการณ์ของคิวสามารถรับได้โดยการเรียก getEventFlagWord() และตัวชี้นั้น (หรือตัวชี้ใด ๆ ไปยังตำแหน่งหน่วยความจำที่ใช้ร่วมกันที่เหมาะสม) สามารถใช้เพื่อสร้างออบเจ็กต์ EventFlag เพื่อส่งผ่านไปยังรูปแบบยาวของ readBlocking() และ writeBlocking() สำหรับคิวอื่น พารามิเตอร์ readNotification และ writeNotification บอกว่าบิตใดในแฟล็กเหตุการณ์ที่ควรใช้เพื่อส่งสัญญาณการอ่านและเขียนบนคิวนั้น readNotification และ writeNotification เป็นบิตมาสก์ 32 บิต

readBlocking() รอบิต writeNotification ; หากพารามิเตอร์นั้นเป็น 0 การโทรจะล้มเหลวเสมอ ถ้าค่า readNotification เป็น 0 การเรียกจะไม่ล้มเหลว แต่การอ่านสำเร็จจะไม่ตั้งค่าบิตการแจ้งเตือนใดๆ ในคิวที่ซิงโครไนซ์ หมายความว่าการเรียก writeBlocking() ที่สอดคล้องกันจะไม่ถูกปลุก เว้นแต่บิตจะถูกตั้งค่าไว้ที่อื่น ในคิวที่ไม่ซิงโครไนซ์ writeBlocking() จะไม่รอ (ควรใช้เพื่อตั้งค่าบิตการแจ้งเตือนการเขียน) และเหมาะสมที่การอ่านจะไม่ตั้งค่าบิตการแจ้งเตือนใดๆ ในทำนองเดียวกัน writeblocking() จะล้มเหลวหาก readNotification เป็น 0 และการเขียนที่สำเร็จจะตั้งค่าบิต writeNotification ที่ระบุ

หากต้องการรอหลายคิวพร้อมกัน ให้ใช้ wait() ของวัตถุ EventFlag เพื่อรอบิตมาสก์ของการแจ้งเตือน wait() วิธีการส่งกลับคำสถานะพร้อมกับบิตที่ทำให้เกิดการตั้งค่าการปลุก ข้อมูลนี้จะใช้เพื่อตรวจสอบว่าคิวที่เกี่ยวข้องนั้นมีพื้นที่หรือข้อมูลเพียงพอสำหรับการดำเนินการเขียน/อ่านที่ต้องการ และดำเนิน write() / read() หากต้องการรับการแจ้งเตือนหลังการดำเนินการ ให้ใช้การเรียกอีกครั้งไปยัง wake() ของ EventFlag สำหรับคำจำกัดความของ EventFlag abstraction โปรดดูที่ system/libfmq/include/fmq/EventFlag.h

การดำเนินการคัดลอกเป็นศูนย์

API read / write / readBlocking / writeBlocking() จะนำตัวชี้ไปยังบัฟเฟอร์อินพุต/เอาท์พุตเป็นอาร์กิวเมนต์ และใช้การเรียก memcpy() ภายในเพื่อคัดลอกข้อมูลระหว่างบัฟเฟอร์เดียวกันและบัฟเฟอร์วงแหวน FMQ เพื่อปรับปรุงประสิทธิภาพ Android 8.0 และสูงกว่าได้รวมชุด API ที่ให้การเข้าถึงตัวชี้โดยตรงไปยังบัฟเฟอร์วงแหวน ทำให้ไม่จำเป็นต้องใช้การเรียก memcpy

ใช้ API สาธารณะต่อไปนี้สำหรับการดำเนินการ FMQ แบบไม่มีสำเนา:

bool beginWrite(size_t nMessages, MemTransaction* memTx) const;
bool commitWrite(size_t nMessages);

bool beginRead(size_t nMessages, MemTransaction* memTx) const;
bool commitRead(size_t nMessages);
  • เมธอด beginWrite จัดเตรียมตัวชี้พื้นฐานลงในบัฟเฟอร์วงแหวน FMQ หลังจากเขียนข้อมูลแล้ว ให้คอมมิตข้อมูลโดยใช้ commitWrite() เมธอด beginRead / commitRead ทำหน้าที่ในลักษณะเดียวกัน
  • เมธอด beginRead / Write ใช้ในการป้อนจำนวนข้อความที่จะอ่าน/เขียน และส่งคืนบูลีนเพื่อระบุว่าสามารถอ่าน/เขียนได้หรือไม่ หากสามารถอ่านหรือเขียนได้ โครงสร้าง memTx จะถูกเติมด้วยพอยน์เตอร์ฐานที่สามารถใช้เพื่อเข้าถึงพอยน์เตอร์โดยตรงในหน่วยความจำแชร์บัฟเฟอร์วงแหวน
  • โครงสร้าง MemRegion ประกอบด้วยรายละเอียดเกี่ยวกับบล็อกหน่วยความจำ รวมถึงตัวชี้ฐาน (ที่อยู่ฐานของบล็อกหน่วยความจำ) และความยาวในรูปของ T (ความยาวของบล็อกหน่วยความจำในแง่ของประเภทคิวข้อความที่กำหนดโดย HIDL)
  • โครงสร้าง MemTransaction มีโครงสร้าง MemRegion สอง first และ second เมื่ออ่านหรือเขียนลงในบัฟเฟอร์วงแหวนอาจต้องมีการพันรอบจุดเริ่มต้นของคิว นี่หมายความว่าจำเป็นต้องใช้ตัวชี้ฐานสองตัวในการอ่าน/เขียนข้อมูลลงในบัฟเฟอร์วงแหวน FMQ

หากต้องการรับที่อยู่ฐานและความยาวจากโครงสร้าง MemRegion :

T* getAddress(); // gets the base address
size_t getLength(); // gets the length of the memory region in terms of T
size_t getLengthInBytes(); // gets the length of the memory region in bytes

หากต้องการรับการอ้างอิงถึง MemRegion ตัวแรกและตัวที่สองภายในวัตถุ MemTransaction :

const MemRegion& getFirstRegion(); // get a reference to the first MemRegion
const MemRegion& getSecondRegion(); // get a reference to the second MemRegion

ตัวอย่างการเขียนไปยัง FMQ โดยใช้ API การคัดลอกเป็นศูนย์:

MessageQueueSync::MemTransaction tx;
if (mQueue->beginRead(dataLen, &tx)) {
    auto first = tx.getFirstRegion();
    auto second = tx.getSecondRegion();

    foo(first.getAddress(), first.getLength()); // method that performs the data write
    foo(second.getAddress(), second.getLength()); // method that performs the data write

    if(commitWrite(dataLen) == false) {
       // report error
    }
} else {
   // report error
}

วิธีการช่วยเหลือต่อไปนี้เป็นส่วนหนึ่งของ MemTransaction :

  • T* getSlot(size_t idx);
    ส่งกลับตัวชี้ไปยังช่อง idx ภายใน MemRegions ที่เป็นส่วนหนึ่งของวัตถุ MemTransaction นี้ ถ้าวัตถุ MemTransaction เป็นตัวแทนของขอบเขตหน่วยความจำเพื่ออ่าน/เขียน N รายการประเภท T ดังนั้นช่วงที่ถูกต้องของ idx จะอยู่ระหว่าง 0 ถึง N-1
  • bool copyTo(const T* data, size_t startIdx, size_t nMessages = 1);
    เขียนรายการ nMessages ประเภท T ลงในพื้นที่หน่วยความจำที่อ็อบเจ็กต์อธิบาย โดยเริ่มจากดัชนี startIdx เมธอดนี้ใช้ memcpy() และไม่ได้มีไว้สำหรับการดำเนินการคัดลอกเป็นศูนย์ ถ้าวัตถุ MemTransaction แสดงถึงหน่วยความจำสำหรับอ่าน/เขียน N รายการประเภท T ดังนั้นช่วงที่ถูกต้องของ idx จะอยู่ระหว่าง 0 ถึง N-1
  • bool copyFrom(T* data, size_t startIdx, size_t nMessages = 1);
    วิธีการช่วยเหลือในการอ่านรายการ nMessages ประเภท T จากขอบเขตหน่วยความจำที่อธิบายโดยอ็อบเจ็กต์ที่เริ่มต้นจาก startIdx เมธอดนี้ใช้ memcpy() และไม่ได้มีไว้สำหรับการดำเนินการคัดลอกเป็นศูนย์

การส่งคิวผ่าน HIDL

ในด้านการสร้าง:

  1. สร้างออบเจ็กต์คิวข้อความตามที่อธิบายไว้ข้างต้น
  2. ตรวจสอบว่าวัตถุนั้นถูกต้องด้วย isValid()
  3. หากคุณกำลังรอหลายคิวโดยส่ง EventFlag ไปเป็นรูปแบบยาวของ readBlocking() / writeBlocking() คุณสามารถแยกตัวชี้แฟล็กเหตุการณ์ (โดยใช้ getEventFlagWord() ) จากอ็อบเจ็กต์ MessageQueue ที่เริ่มต้นเพื่อสร้างแฟล็ก และใช้แฟล็กนั้นเพื่อสร้างออบเจ็กต์ EventFlag ที่จำเป็น
  4. ใช้เมธอด MessageQueue getDesc() เพื่อรับวัตถุ descriptor
  5. ในไฟล์ .hal ให้กำหนดพารามิเตอร์ประเภท fmq_sync ให้กับเมธอด หรือ fmq_unsync โดยที่ T เป็นประเภทที่กำหนด HIDL ที่เหมาะสม ใช้สิ่งนี้เพื่อส่งอ็อบเจ็กต์ที่ส่งคืนโดย getDesc() ไปยังกระบวนการรับ

ทางด้านรับ:

  1. ใช้วัตถุคำอธิบายเพื่อสร้างวัตถุ MessageQueue ตรวจสอบให้แน่ใจว่าใช้คิวรสชาติและประเภทข้อมูลเดียวกัน ไม่เช่นนั้นเทมเพลตจะไม่สามารถคอมไพล์ได้
  2. ถ้าคุณแยกค่าสถานะเหตุการณ์ แยกค่าสถานะออกจากวัตถุ MessageQueue ที่สอดคล้องกันในกระบวนการรับ
  3. ใช้วัตถุ MessageQueue เพื่อถ่ายโอนข้อมูล