إذا كنت تبحث عن دعم AIDL، فراجع أيضًا FMQ مع AIDL .
تستخدم البنية التحتية لاستدعاء الإجراء عن بعد (RPC) لـ HIDL آليات Binder، مما يعني أن الاستدعاءات تتضمن حملًا زائدًا، وتتطلب عمليات kernel، وقد تؤدي إلى تشغيل إجراء جدولة. ومع ذلك، بالنسبة للحالات التي يجب فيها نقل البيانات بين العمليات بحمل أقل وبدون مشاركة kernel، يتم استخدام نظام قائمة انتظار الرسائل السريعة (FMQ).
يقوم FMQ بإنشاء قوائم انتظار الرسائل بالخصائص المطلوبة. يمكن إرسال كائن MQDescriptorSync
أو MQDescriptorUnsync
عبر استدعاء HIDL RPC واستخدامه بواسطة عملية الاستلام للوصول إلى قائمة انتظار الرسائل.
يتم دعم قوائم انتظار الرسائل السريعة فقط في لغة C++ وعلى الأجهزة التي تعمل بنظام التشغيل Android 8.0 والإصدارات الأحدث.
أنواع قائمة انتظار الرسائل
يدعم Android نوعين من قوائم الانتظار (المعروفين باسم النكهات ):
- يُسمح لقوائم الانتظار غير المتزامنة بالتجاوز، ويمكن أن تحتوي على العديد من القراء؛ يجب على كل قارئ قراءة البيانات في الوقت المناسب وإلا فقدها.
- لا يُسمح بتجاوز قوائم الانتظار المتزامنة ، ويمكن أن تحتوي على قارئ واحد فقط.
لا يُسمح لكلا النوعين من قوائم الانتظار بالتدفق (ستفشل القراءة من قائمة انتظار فارغة) ويمكن أن يكون لهما كاتب واحد فقط.
غير متزامن
تحتوي قائمة الانتظار غير المتزامنة على كاتب واحد فقط، ولكن يمكن أن تحتوي على أي عدد من القراء. يوجد موضع كتابة واحد لقائمة الانتظار؛ ومع ذلك، يتتبع كل قارئ موضع القراءة المستقل الخاص به.
تنجح دائمًا عمليات الكتابة إلى قائمة الانتظار (لا يتم التحقق من تجاوز السعة) طالما أنها ليست أكبر من سعة قائمة الانتظار التي تم تكوينها (تفشل عمليات الكتابة الأكبر من سعة قائمة الانتظار على الفور). نظرًا لأن كل قارئ قد يكون له موضع قراءة مختلف، بدلاً من انتظار كل قارئ لقراءة كل جزء من البيانات، يُسمح للبيانات بالسقوط من قائمة الانتظار عندما تحتاج عمليات الكتابة الجديدة إلى المساحة.
يتحمل القراء مسؤولية استرداد البيانات قبل أن تقع في نهاية قائمة الانتظار. القراءة التي تحاول قراءة بيانات أكثر مما هو متاح إما تفشل على الفور (إذا لم تكن محظورة) أو تنتظر توفر بيانات كافية (إذا كانت محظورة). القراءة التي تحاول قراءة بيانات أكثر من سعة قائمة الانتظار تفشل دائمًا على الفور.
إذا فشل القارئ في مواكبة الكاتب، بحيث تكون كمية البيانات المكتوبة والتي لم يقرأها هذا القارئ بعد أكبر من سعة قائمة الانتظار، فإن القراءة التالية لا تُرجع البيانات؛ بدلاً من ذلك، يقوم بإعادة تعيين موضع القراءة للقارئ ليعادل موضع الكتابة الأخير ثم يُرجع الفشل. إذا تم فحص البيانات المتاحة للقراءة بعد تجاوز السعة ولكن قبل القراءة التالية، فإنها تعرض بيانات متاحة للقراءة أكثر من سعة قائمة الانتظار، مما يشير إلى حدوث تجاوز. (إذا تجاوزت قائمة الانتظار بين التحقق من البيانات المتاحة ومحاولة قراءة تلك البيانات، فإن الإشارة الوحيدة لتجاوز السعة هي فشل القراءة.)
من المحتمل ألا يرغب قراء قائمة الانتظار غير المتزامنة في إعادة تعيين مؤشرات القراءة والكتابة الخاصة بقائمة الانتظار. لذلك، عند إنشاء قائمة الانتظار من الواصفات، يجب على القراء استخدام وسيطة "خطأ" للمعلمة "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
كما هو موضح في هذا القسم.
حظر قوائم الانتظار وأعلام الأحداث
بشكل افتراضي، لا تدعم قوائم الانتظار حظر عمليات القراءة/الكتابة. هناك نوعان من حظر مكالمات القراءة/الكتابة:
- نموذج قصير ، مع ثلاثة معلمات (مؤشر البيانات، عدد العناصر، المهلة). يدعم حظر عمليات القراءة/الكتابة الفردية في قائمة انتظار واحدة. عند استخدام هذا النموذج، ستتعامل قائمة الانتظار مع علامة الحدث والأقنعة النقطية داخليًا، ويجب تهيئة كائن قائمة انتظار الرسائل الأول بمعلمة ثانية من
true
. على سبيل المثال:// For an unsynchronized FMQ that supports blocking mFmqUnsynchronizedBlocking = new (std::nothrow) MessageQueue<uint16_t, kUnsynchronizedWrite> (kNumElementsInQueue, true /* enable blocking operations */);
- نموذج طويل ، مع ستة معلمات (تتضمن إشارة الحدث والأقنعة النقطية). يدعم استخدام كائن
EventFlag
مشترك بين قوائم انتظار متعددة ويسمح بتحديد أقنعة بت الإشعارات المراد استخدامها. في هذه الحالة، يجب توفير علامة الحدث والأقنعة النقطية لكل استدعاء للقراءة والكتابة.
بالنسبة للنموذج الطويل، يمكن توفير EventFlag
بشكل صريح في كل استدعاء readBlocking()
و writeBlocking()
. قد تتم تهيئة إحدى قوائم الانتظار باستخدام علامة حدث داخلية، والتي يجب بعد ذلك استخراجها من كائنات MessageQueue
هذه باستخدام getEventFlagWord()
واستخدامها لإنشاء كائنات EventFlag
في كل عملية لاستخدامها مع FMQs الأخرى. وبدلاً من ذلك، يمكن تهيئة كائنات 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
هي:
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
، راجع system/libfmq/include/fmq/EventFlag.h
.
عمليات النسخ صفر
تأخذ واجهات برمجة التطبيقات read
/ write
/ readBlocking
/ writeBlocking()
مؤشرًا إلى المخزن المؤقت للإدخال / الإخراج كوسيطة وتستخدم استدعاءات memcpy()
داخليًا لنسخ البيانات بين نفس المخزن المؤقت وحلقة FMQ. لتحسين الأداء، يتضمن نظام التشغيل Android 8.0 والإصدارات الأحدث مجموعة من واجهات برمجة التطبيقات التي توفر وصولاً مباشرًا للمؤشر إلى المخزن المؤقت للحلقة، مما يلغي الحاجة إلى استخدام مكالمات memcpy
.
استخدم واجهات برمجة التطبيقات العامة التالية لعمليات 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 باستخدام واجهات برمجة التطبيقات ذات النسخة الصفرية:
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
ومن ناحية الخلق:
- قم بإنشاء كائن قائمة انتظار الرسائل كما هو موضح أعلاه.
- تحقق من صحة الكائن باستخدام
isValid()
. - إذا كنت ستنتظر قوائم انتظار متعددة عن طريق تمرير
EventFlag
إلى الشكل الطويل منreadBlocking()
/writeBlocking()
، فيمكنك استخراج مؤشر علامة الحدث (باستخدامgetEventFlagWord()
) من كائنMessageQueue
الذي تمت تهيئته لإنشاء العلامة، واستخدم هذه العلامة لإنشاء كائنEventFlag
الضروري. - استخدم طريقة
MessageQueue
getDesc()
للحصول على كائن واصف. - في ملف
.hal
، قم بإعطاء الطريقة معلمة من النوعfmq_sync
أو fmq_unsync
حيث T
هو نوع مناسب محدد بواسطة HIDL. استخدم هذا لإرسال الكائن الذي تم إرجاعه بواسطةgetDesc()
إلى عملية الاستلام.
من ناحية الاستلام:
- استخدم كائن الواصف لإنشاء كائن
MessageQueue
. تأكد من استخدام نفس نكهة قائمة الانتظار ونوع البيانات، وإلا سيفشل تجميع القالب. - إذا قمت باستخراج علامة حدث، فاستخرج العلامة من كائن
MessageQueue
المطابق في عملية الاستلام. - استخدم كائن
MessageQueue
لنقل البيانات.