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

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

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

ประเภทคิว

Android รองรับคิว 2 ประเภท (เรียกว่าตัวแปร) ดังนี้

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

ทั้ง 2 ประเภทของคิวไม่อนุญาตให้เกิด Underflow (อ่านจากคิวว่างไม่สำเร็จ) และมีผู้เขียนได้เพียงคนเดียว

คิวที่ไม่ได้ซิงค์

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

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

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

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

คิวที่ซิงค์

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

ตั้งค่า FMQ

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

สร้างออบเจ็กต์ 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 nonblocking 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 ที่ 2

ระบบจะสร้างอีกด้านหนึ่งของคิวข้อความโดยใช้ออบเจ็กต์ MQDescriptor ที่ได้มาจากอีกด้านหนึ่ง ระบบจะส่งออบเจ็กต์ MQDescriptor ผ่านคอล HIDL หรือ AIDL RPC ไปยังกระบวนการที่ถือปลายทางที่ 2 ของคิวข้อความ ไฟล์ 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 ทั้งหมดตามที่อธิบายในส่วนนี้

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

โดยค่าเริ่มต้น คิวจะไม่รองรับการอ่านและการเขียนแบบบล็อก การบล็อกการเรียกใช้การอ่านและการเขียนมี 2 ประเภท ได้แก่

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

สำหรับแบบฟอร์มแบบยาว คุณสามารถระบุ EventFlag อย่างชัดเจนในreadBlocking() และ writeBlocking() คุณสามารถเริ่มต้นคิวรายการใดรายการหนึ่งด้วย Flag เหตุการณ์ภายใน ซึ่งจะต้องดึงมาจากออบเจ็กต์ 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)
  • ผู้อ่าน: สร้างคิวข้อความด้วย 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 หมายความว่าจะไม่หมดเวลา)

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

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

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

การดำเนินการแบบคัดลอกศูนย์ข้อมูล

วิธีการ 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 2 รายการ ได้แก่ first และ second เนื่องจากการอ่านหรือเขียนลงในบัฟเฟอร์แบบวงแหวนอาจต้องมีการตัดไปไว้ที่จุดเริ่มต้นของคิว ซึ่งหมายความว่าต้องใช้เคอร์เซอร์ฐาน 2 ตัวในการอ่านและเขียนข้อมูลลงในบัฟเฟอร์แบบวงแหวน 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 รายการแรกและที่ 2 ภายในออบเจ็กต์ 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 แสดงหน่วยความจําสําหรับอ่านและเขียนรายการประเภท T จำนวน N รายการ ช่วงของ 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() คุณสามารถดึงข้อมูลเคอร์เซอร์ Flag เหตุการณ์ (โดยใช้ getEventFlagWord()) จากออบเจ็กต์ MessageQueue ที่เริ่มต้นเพื่อสร้าง Flag และนำ Flag นั้นไปสร้างออบเจ็กต์ EventFlag ที่จำเป็น
  4. ใช้เมธอด MessageQueue getDesc() เพื่อรับออบเจ็กต์ข้อบ่งชี้
  5. ในไฟล์ HAL ให้ตั้งค่าพารามิเตอร์ของเมธอดเป็นประเภท fmq_sync หรือ fmq_unsync โดยที่ T เป็นประเภทที่ HIDL กำหนดไว้ซึ่งเหมาะสม ใช้เพื่อส่งออบเจ็กต์ที่ getDesc() แสดงผลไปยังกระบวนการรับ

ฝั่งที่รับ

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