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

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

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

ประเภทคิว

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

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

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

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

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

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

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

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

คิวที่ซิงค์

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

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

หากต้องการรอหลายคิวพร้อมกัน ให้ใช้เมธอด wait() ของออบเจ็กต์ EventFlag เพื่อรอการแจ้งเตือนบิตมาสก์ เมธอด 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 เพื่อโอนข้อมูล