تستخدِم بنية HIDL الأساسية لطلبات الإجراءات عن بُعد (RPC) آليات الربط، ما يعني أنّ طلبات الإجراءات تتطلّب وقتًا إضافيًا وتتطلّب عمليات في النواة ويمكن أن تؤدي إلى بدء إجراء جدولة. ومع ذلك، في الحالات التي يجب فيها نقل البيانات بين العمليات مع تقليل الوقت المستغرَق وبدون تدخل من نظام التشغيل، يتم استخدام نظام "قائمة الرسائل السريعة" (FMQ).
تُنشئ واجهة FMQ قوائم انتظار الرسائل باستخدام السمات المطلوبة. يمكنك إرسال ملف MQDescriptorSync
أو MQDescriptorUnsync
عبر طلب HIDL RPC، وتستخدم العملية المستلِمة العنصر للوصول إلى ملف MQDescriptorSync
قائمة الرسائل.
أنواع قوائم الانتظار
يتيح Android نوعَين من قوائم الانتظار (المعروفَين باسم النُسخ):
- يُسمح بتجاوز سعة قوائم الانتظار غير المتزامنة، ويمكن أن تتضمّن العديد من القارئين، ويجب أن يقرأ كل قارئ البيانات في الوقت المناسب وإلا سيفقدها.
- لا يُسمح بتجاوز سعة قوائم الانتظار المتزامنة، ويمكن أن يكون لها قارِئ واحد فقط.
لا يُسمح لكلا نوعَي الانتظار بانخفاض عدد العناصر إلى ما دون الحد الأدنى (تؤدي القراءة من قائمة انتظار فارغة إلى حدوث خطأ)، ويمكن أن يتضمّن كلّ نوع كاتبًا واحدًا فقط.
قوائم الانتظار غير المتزامنة
تحتوي "القائمة الانتظار غير المتزامنة" على كاتب واحد فقط، ولكن يمكن أن تحتوي على أي عدد من القارئين. هناك موضع كتابة واحد للانتظار، ومع ذلك، يحتفظ كل قارئ بموضع القراءة المستقل الخاص به.
تنجح عمليات الكتابة إلى "القائمة الانتظار" دائمًا (لا يتم التحقّق من حدوث تدفّق زائد) ما دام حجمها ليس أكبر من سعة "القائمة الانتظار" التي تم ضبطها (تتعطّل عمليات الكتابة التي تزيد عن سعة "القائمة الانتظار" على الفور). وبما أنّ كل قارئ قد يكون لديه موضع قراءة مختلف، بدلاً من الانتظار إلى أن يقرأ كل قارئ كل قطعة من البيانات، يتمّ إزالة البيانات من "قائمة الانتظار" كلما احتجت عمليات الكتابة الجديدة إلى المساحة.
يتحمّل القرّاء مسؤولية استرداد البيانات قبل أن تسقط من نهاية القائمة. إذا حاولت قراءة بيانات أكثر من تلك المتوفّرة، إما أن تتعذّر القراءة على الفور (إذا كانت غير محظورة) أو تنتظر إلى أن تصبح بيانات كافية متاحة (إذا كانت محظورة). إنّ عملية القراءة التي تحاول قراءة بيانات أكثر من سعة الانتظار تتعذّر دائمًا على الفور.
إذا تعذّر على أحد القراء مواكبة الكاتب، بحيث تتجاوز كمية البيانات المكتوبة والتي لم يقرأها هذا القارئ بعد سعة الانتظار، لن تعرض عملية القراءة التالية البيانات، بل ستعيد ضبط موضع قراءة القارئ على موضع الكتابة بالإضافة إلى نصف السعة، ثم تعرِض خطأ. ويؤدي ذلك إلى ترك نصف مساحة التخزين المؤقتة متاحة للقراءة ويحجز مساحة للعمليات الجديدة لكتابة البيانات لتجنُّب تجاوز السعة القصوى للانتظار مرة أخرى على الفور. في حال التحقّق من البيانات المتاحة للقراءة بعد حدوث تدفّق بيانات زائد ولكن قبل القراءة التالية، يُظهر العنصر المزيد من البيانات المتاحة للقراءة مقارنةً بسعة الانتظار، ما يشير إلى حدوث تدفّق بيانات زائد. (إذا امتلأت قائمة الانتظار بين التحقّق من البيانات المتاحة ومحاولة قراءة تلك البيانات، فإنّ المؤشر الوحيد على الامتلاء هو تعذُّر القراءة).
قوائم الانتظار المتزامنة
تحتوي "القائمة الانتظار المتزامنة" على كاتب واحد وقارئ واحد مع موضع كتابة واحد وموضع قراءة واحد. من المستحيل كتابة بيانات أكثر مما يمكن أن تستوعبه الطابور أو قراءة بيانات أكثر مما يحتوي عليه حاليًا. استنادًا إلى ما إذا تم استدعاء دالة القراءة أو الكتابة المحظورة أو غير المحظورة، تؤدي محاولات تجاوز المساحة أو البيانات المتاحة إلى تعذُّر إكمال العملية أو حظرها على الفور إلى أن تتمكّن من إكمال العملية المطلوبة. إنّ محاولات قراءة أو كتابة بيانات أكثر من سعة الانتظار تؤدي دائمًا إلى تعذُّر إكمالها على الفور.
إعداد طلب معلومات فورية
تتطلّب قائمة الرسائل الواردة عناصر 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 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 الثاني.
يتم إنشاء الجانب الثاني من قائمة انتظار الرسائل باستخدام كائن
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
في كل عملية لاستخدامها مع قوائم 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
واجهة برمجة التطبيقات العامة للكائن 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
.
للانتظار في قوائم انتظار متعددة في الوقت نفسه، استخدِم EventFlag
wait()
في كائن EventFlag
للانتظار في قناع بتات للإشعارات. تُرجع الطريقة
wait()
كلمة حالة تتضمّن الوحدات التي أدّت إلى ضبط
wake up. وتُستخدَم هذه المعلومات بعد ذلك للتحقّق من توفّر
مساحة أو بيانات كافية في "القائمة الانتظار" المقابلة لإجراء عملية الكتابة والقراءة المطلوبة وتنفيذ write()
وread()
غير المحظورة. للحصول على إعلام
بعملية نشر، استخدِم طلبًا آخر لطريقة
wake()
في عنصر EventFlag
. للاطّلاع على تعريف EventFlag
التجريد، يُرجى الاطّلاع على
system/libfmq/include/fmq/EventFlag.h
.
عمليات النسخ بدون أي تكلفة
تأخذ الدوالّ
read
وwrite
وreadBlocking
وwriteBlocking()
معلمة تشير إلى مخزن مؤقت للدخل والخرج، وتستخدم طلبات
memcpy()
داخليًا لنسخ البيانات بين المخزن المؤقت نفسه و
مخزن FMQ الدائري. لتحسين الأداء، يتضمّن الإصدار 8.0 من نظام التشغيل Android والإصدارات الأحدث مجموعة من واجهتَي برمجة التطبيقات (API) اللتين توفّران إمكانية وصول المؤشر مباشرةً إلى المخزن الدائري، ما يزيل الحاجة إلى استخدام طلبات memcpy
.
استخدِم واجهات برمجة التطبيقات العامة التالية لإجراء عمليات "الطلبات المجمّعة بدون نسخ":
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
. استخدِم نوع البيانات ونكهة "القائمة الانتظار" نفسها، وإلا لن يتم compiling (تجميع) النموذج. - إذا استخرجت علامة حدث، استخرِج العلامة من عنصر
MessageQueue
المقابل في عملية الاستقبال. - استخدِم عنصر
MessageQueue
لنقل البيانات.