Wenn Sie AIDL-Unterstützung benötigen, lesen Sie auch FMQ mit AIDL
Die Infrastruktur von HIDL für Remoteprozeduraufrufe (Remote Procedure Call, RPC) nutzt Binder-Mechanismen, Das bedeutet, dass Aufrufe mit Aufwand verbunden sind, Kernel-Vorgänge erfordern und möglicherweise Planeraktion. Wenn jedoch Daten zwischen verschiedenen mit weniger Overhead und ohne Kernel-Beteiligung, ist die Fast Message Queue verwendet wird.
FMQ erstellt Nachrichtenwarteschlangen mit den gewünschten Attributen. Eine
Das MQDescriptorSync
- oder MQDescriptorUnsync
-Objekt kann
die über einen HIDL RPC-Aufruf gesendet und vom empfangenden Prozess für den Zugriff
Nachrichtenwarteschlange.
Schnelle Nachrichtenwarteschlangen werden nur in C++ und auf Geräten unterstützt. mit Android 8.0 oder höher.
MessageQueue-Typen
Android unterstützt zwei Warteschlangentypen (sogenannte flavors):
- Nicht synchronisierte Warteschlangen können einen Überlauf ausführen und viele Lesern; Jeder Leser muss Daten rechtzeitig lesen oder verlieren.
- Synchronisierte Warteschlangen dürfen nicht überlaufen und dürfen einem einzigen Leser.
Beide Warteschlangentypen dürfen keinen Unterlauf haben (Lesezugriff aus einer leeren Warteschlange) schlägt fehl) und kann nur einen Writer haben.
Nicht synchronisiert
Eine nicht synchronisierte Warteschlange hat nur einen Writer, kann aber beliebig viele Lesern. Es gibt eine Schreibposition für die Warteschlange: Jeder Leser sieht sich jedoch unabhängig von Leseposition.
Schreibvorgänge in die Warteschlange sind immer erfolgreich (nicht auf Überlauf überprüft), solange Folgendes gilt: sie nicht größer sind als die konfigurierte Warteschlangenkapazität (Schreibvorgänge sind größer als die Warteschlangenkapazität sofort ausfallen). Jeder Leser kann verschiedene Lesematerialien anstatt darauf zu warten, dass jeder Leser alle Daten liest, kann aus der Warteschlange ausfallen, wenn neue Schreibvorgänge den Speicherplatz benötigen.
Leser sind dafür verantwortlich, Daten abzurufen, bevor diese am Ende des in der Warteschlange. Ein Lesevorgang, der versucht, mehr Daten zu lesen, als verfügbar sind schlägt sofort fehl (bei Nichtblockierung) oder wartet, bis genügend Daten verfügbar sind (falls Blockierung). Lesevorgang, der immer versucht, mehr Daten als die Warteschlangenkapazität zu lesen schlägt sofort fehl.
Wenn Lesende nicht mit dem Schreibenden Schritt halten können, sodass die Datenmenge die von diesem Lesegerät geschrieben und noch nicht gelesen wurden, größer als die Kapazität der Warteschlange ist, Beim nächsten Lesevorgang werden keine Daten zurückgegeben. wird der Lesemodus des Lesers zurückgesetzt, Position, die der letzten Schreibposition entspricht, und gibt dann "Fehler" zurück. Wenn die wird nach dem Überlauf geprüft, aber vor dem nächsten Lesen mehr Daten zum Lesen als die Warteschlangenkapazität anzeigt, Überlauf ist aufgetreten. (Wenn die Warteschlange zwischen der Überprüfung verfügbarer Daten überläuft und versucht, diese Daten zu lesen, ist das einzige Anzeichen für Overflow, dass der kann nicht gelesen werden.)
Leser einer nicht synchronisierten Warteschlange möchten die Warteschlange wahrscheinlich nicht zurücksetzen. die Lese- und Schreibzeiger der Warteschlange. Wenn Sie die Warteschlange über das Deskriptorlesegeräte sollten für „resetPointers“ das Argument „false“ verwenden .
Synchronisiert
Eine synchronisierte Warteschlange hat einen Writer und einen Reader mit einem einzigen Schreibvorgang und einer Leseposition. Es ist unmöglich, mehr Daten als Die Warteschlange verfügt über mehr Daten oder hat mehr Daten gelesen, als sie derzeit enthält. Je nachdem, ob die blockierende oder nicht blockierende Schreib- oder Lesefunktion aufgerufen, wird versucht, den verfügbaren Speicherplatz zu überschreiten, oder es werden Fehler zurückgegeben. oder blockieren, bis der gewünschte Vorgang abgeschlossen ist. Versuche, dass mehr Daten gelesen oder geschrieben werden, als die Kapazität der Warteschlange erreicht, schlägt immer sofort fehl.
FMQ einrichten
Für eine Nachrichtenwarteschlange sind mehrere MessageQueue
-Objekte erforderlich: eines bis
in die geschrieben werden und aus denen mindestens eins gelesen werden soll. Es gibt keine explizite
Konfiguration des Objekts, das zum Schreiben oder Lesen verwendet wird liegt es an der
verwenden, um sicherzustellen, dass kein Objekt
gleichzeitig zum Lesen und Schreiben verwendet wird,
entspricht höchstens einem Schreiber und bei synchronisierten Warteschlangen maximal einen
Leser.
Erstes MessageQueue-Objekt erstellen
Eine Nachrichtenwarteschlange wird in einem einzigen Aufruf erstellt und konfiguriert:
#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)
-Initialisierer erstellt und initialisiert ein -Objekt, das die Funktionalität der Nachrichtenwarteschlange unterstützt.- Der
MessageQueue<T, flavor>(numElements, configureEventFlagWord)
-Initialisierer erstellt und initialisiert ein Objekt die die Blockierfunktion der Nachrichtenwarteschlange unterstützt. flavor
kann entwederkSynchronizedReadWrite
für synchronisierte Warteschlange oderkUnsynchronizedWrite
für eine nicht synchronisierte in die Warteschlange stellen.uint16_t
(in diesem Beispiel) kann eine beliebige HIDL-definierter Typ, der beinhaltet keine verschachtelten Zwischenspeicher (keinestring
odervec
-Typen), Handles oder Schnittstellen.kNumElementsInQueue
gibt die Größe der Warteschlange in Form von Einträge; bestimmt die Größe des Zwischenspeichers des gemeinsamen Arbeitsspeichers, der für die Warteschlange.
Zweites MessageQueue-Objekt erstellen
Die zweite Seite der Nachrichtenwarteschlange wird mithilfe eines
MQDescriptor
-Objekt, das von der ersten Seite abgerufen wird. Die
Das MQDescriptor
-Objekt wird über einen HIDL- oder AIDL-RPC-Aufruf an den Prozess gesendet
an dem sich das zweite Ende der
Nachrichtenwarteschlange befindet. Die
MQDescriptor
enthält unter anderem folgende Informationen zur Warteschlange:
- Informationen zum Zuordnen des Zwischenspeichers und Schreibzeigers.
- Informationen zum Zuordnen des Lesezeigers (wenn die Warteschlange synchronisiert ist).
- Informationen zum Zuordnen des Ereignis-Flag-Worts (wenn die Warteschlange blockiert)
- Objekttyp (
<T, flavor>
), einschließlich des HIDL-definierter Typ von und der Warteschlangen-Flavor (synchron oder nicht synchronisiert) zu definieren.
Mit dem MQDescriptor
-Objekt kann ein
MessageQueue
-Objekt:
MessageQueue<T, flavor>::MessageQueue(const MQDescriptor<T, flavor>& Desc, bool resetPointers)
Der Parameter resetPointers
gibt an, ob der Lesevorgang zurückgesetzt werden soll
und die Positionen in 0 schreiben, während Sie dieses MessageQueue
-Objekt erstellen.
In einer nicht synchronisierten Warteschlange ist die Leseposition (die zu jedem
MessageQueue
-Objekt in nicht synchronisierten Warteschlangen) ist immer auf 0 festgelegt
während der Erstellung. Normalerweise wird MQDescriptor
während
Erstellung des ersten Warteschlangenobjekts. Für zusätzliche Kontrolle über die gemeinsamen
Arbeitsspeicher hinzufügen, kannst du den MQDescriptor
manuell einrichten
(MQDescriptor
ist definiert in
system/libhidl/base/include/hidl/MQDescriptor.h
)
Erstellen Sie dann jedes MessageQueue
-Objekt wie in diesem Abschnitt beschrieben.
Warteschlangen und Ereignis-Flags blockieren
Standardmäßig unterstützen Warteschlangen das Blockieren von Lese-/Schreibvorgängen nicht. Es gibt zwei Arten von blockierender Lese-/Schreibaufrufe:
- Kurzform mit drei Parametern (Datenpunkt, Anzahl der Elemente,
Zeitüberschreitung). Unterstützt das Blockieren einzelner Lese-/Schreibvorgänge auf einem einzigen
in die Warteschlange stellen. Bei Verwendung dieses Formulars verarbeitet die Warteschlange das Ereignis-Flag und die Bitmasken
intern und das erste Warteschlangenobjekt muss
mit dem zweiten Parameter
true
initialisiert werden. Hier einige Beispiele:// For an unsynchronized FMQ that supports blocking mFmqUnsynchronizedBlocking = new (std::nothrow) MessageQueue<uint16_t, kUnsynchronizedWrite> (kNumElementsInQueue, true /* enable blocking operations */);
- Lange Version mit sechs Parametern (einschließlich Ereignis-Flag und Bitmasken).
Unterstützt die Verwendung eines gemeinsamen
EventFlag
-Objekts für mehrere Warteschlangen und ermöglicht die Angabe der zu verwendenden Bitmasken für Benachrichtigungen. In diesem Fall Ereignis-Flag und Bitmasken müssen an jeden Lese- und Schreibaufruf übergeben werden.
Für die Langform kann das EventFlag
explizit in
readBlocking()
- und writeBlocking()
-Aufruf. Eine von
Die Warteschlangen können mit einem internen Ereignis-Flag initialisiert werden, das dann
extrahiert aus den MessageQueue
-Objekten dieser Warteschlange mithilfe von
getEventFlagWord()
und zum Erstellen von EventFlag
verwendet
-Objekte in jedem Prozess zur Verwendung mit anderen FMQs. Die Methode
EventFlag
Objekte können mit beliebigen geeigneten freigegebenen Elementen initialisiert werden.
zu speichern.
Im Allgemeinen sollte für jede Warteschlange nur eine nicht blockierende, kurze Blockierung von Videos im Langformat. Es ist kein Fehler, sie zu mischen, eine Programmierung erforderlich ist, um das gewünschte Ergebnis zu erzielen.
Erinnerung als schreibgeschützt markieren
Standardmäßig hat der gemeinsame Arbeitsspeicher Lese- und Schreibberechtigungen. Für nicht synchronisierte
Warteschlangen (kUnsynchronizedWrite
) möchten, kann der Autor die Schreibberechtigungen für alle
der Leser, bevor er die MQDescriptorUnsync
-Objekte ausgibt. So wird sichergestellt, dass die anderen
Prozesse können nicht in die Warteschlange schreiben. Dies wird zum Schutz vor Fehlern oder unerwünschtem Verhalten
was die Lesenden verarbeiten.
Wenn der Autor möchte, dass die Leser die Warteschlange zurücksetzen können, sobald sie die
MQDescriptorUnsync
zum Erstellen der Leseseite der Warteschlange. Der Speicher kann dann nicht markiert werden.
schreibgeschützt sein. Dies ist das Standardverhalten des Konstruktors "MessageQueue". Wenn es also bereits
bestehenden Usern dieser Warteschlange verwendet werden, muss ihr Code geändert werden, um die Warteschlange mit
resetPointer=false
- Autor:
ashmem_set_prot_region
mit dem DateideskriptorMQDescriptor
aufrufen und Region, die schreibgeschützt sind (PROT_READ
):int res = ashmem_set_prot_region(mqDesc->handle->data[0], PROT_READ)
- Reader: Erstellen Sie eine Nachrichtenwarteschlange mit
resetPointer=false
(die Standardeinstellung isttrue
):mFmq = new (std::nothrow) MessageQueue(mqDesc, false);
MessageQueue verwenden
Die öffentliche API des MessageQueue
-Objekts ist:
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()
und availableToRead()
können verwendet werden
um zu ermitteln, wie viele Daten in einem einzigen Vorgang übertragen werden können. In einer
nicht synchronisierte Warteschlange:
availableToWrite()
gibt immer die Kapazität der Warteschlange zurück.- Jeder Leser hat seine eigene Leseposition und berechnet
availableToRead()
- Aus Sicht eines langsamen Lesers darf die Warteschlange überlaufen.
Dies kann dazu führen, dass
availableToRead()
einen Wert zurückgibt, der größer ist als die Größe der Warteschlange. Der erste Lesevorgang nach einem Überlauf schlägt fehl und führt zu dass die Leseposition für diesen Leser dem aktuellen Schreibzeiger entspricht. ob der Überlauf gemeldet wurde oder nichtavailableToRead()
Die Methoden read()
und write()
geben
true
, wenn alle angeforderten Daten von/zu/von dieser übertragen werden konnten (und wurden)
in der Warteschlange. Diese Methoden blockieren Folgendes nicht: entweder erfolgreich
true
) oder eine fehlgeschlagene Rückgabe (false
) sofort erfolgt.
Die Methoden readBlocking()
und writeBlocking()
warten
bis der angeforderte Vorgang abgeschlossen werden kann oder bis das Zeitlimit (ein
Der timeOutNanos
-Wert 0 bedeutet, dass keine Zeitüberschreitung auftritt).
Blockierende Vorgänge werden mit einem Ereignis-Flag-Wort implementiert. Standardmäßig
jede Warteschlange erstellt und verwendet ihr eigenes Flagswort, um die Kurzform
readBlocking()
und writeBlocking()
. Es ist möglich, dass
mehrere Warteschlangen für ein einzelnes Wort, damit ein Prozess auf Schreibvorgänge oder
Lesevorgänge in eine der Warteschlangen. Ein Zeiger auf das Ereignis-Flag-Wort einer Warteschlange
durch Aufrufen von getEventFlagWord()
und diesem Zeiger (oder einem beliebigen
Zeiger zu einem geeigneten Speicherort des gemeinsamen Speichers), können verwendet werden,
EventFlag
-Objekt, das in die Long-Form übergeben wird
readBlocking()
und writeBlocking()
für eine andere
in die Warteschlange stellen. readNotification
und writeNotification
bestimmen, welche Bits im Ereignis-Flag verwendet werden sollen, um Lesevorgänge und
in diese Warteschlange schreibt. readNotification
und
writeNotification
sind 32-Bit-Bitmasken.
readBlocking()
wartet auf die writeNotification
-Bits.
Wenn dieser Parameter 0 ist, schlägt der Aufruf immer fehl. Wenn die
readNotification
den Wert 0 hat, schlägt der Aufruf nicht fehl, aber ein
Durch erfolgreiches Lesen
werden keine Benachrichtigungsbits gesetzt. In einer synchronisierten Warteschlange
würde der entsprechende writeBlocking()
-Aufruf
wird nie aktiviert, es sei denn, das Bit ist an anderer Stelle gesetzt. In einer nicht synchronisierten Warteschlange
writeBlocking()
wartet nicht (sollte trotzdem verwendet werden, um
Schreiben von Benachrichtigungsbit) und es eignet sich für Lesevorgänge, um keine
Benachrichtigungs-Bits. Gleichermaßen schlägt writeblocking()
fehl, wenn
readNotification
ist 0 und ein erfolgreicher Schreibvorgang legt die angegebene
writeNotification
Bit.
Wenn Sie auf mehrere Warteschlangen gleichzeitig warten möchten, verwenden Sie die Methode des EventFlag
-Objekts
wait()
-Methode, um auf eine Bitmaske mit Benachrichtigungen zu warten. Die
Die Methode wait()
gibt ein Statuswort mit den Bits zurück, die den
zum Aufwachen eingerichtet sein. Anhand dieser Informationen wird dann geprüft, ob die entsprechende Warteschlange
genügend Speicherplatz oder Daten für den gewünschten Schreib-/Lesevorgang
write()
/read()
nicht blockieren. So rufen Sie einen Vorgang zum Hochladen ab:
Benachrichtigung: Anderen Anruf von EventFlag
verwenden
wake()
-Methode. Für eine Definition von EventFlag
Abstraktion, beziehen sich
system/libfmq/include/fmq/EventFlag.h
Null-Kopiervorgänge
Die
read
/write
/readBlocking
/writeBlocking()
APIs nehmen einen Zeiger auf einen Eingabe-/Ausgabepuffer als Argument und verwenden
memcpy()
ruft intern auf, Daten zwischen der gleichen und der
FMQ-Ringzwischenspeicher. Zur Verbesserung der Leistung enthalten Android 8.0 und höher eine Reihe von
APIs, die direkten Zeigerzugriff auf den Ringzwischenspeicher ermöglichen,
memcpy
-Aufrufe erforderlich.
Verwenden Sie die folgenden öffentlichen APIs für FMQ-Zero-Copy-Vorgänge:
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);
- Die Methode
beginWrite
stellt Basiszeiger für den FMQ-Ring bereit Puffer. Nachdem die Daten geschrieben wurden, führen Sie mitcommitWrite()
ein Commit durch. Die MethodenbeginRead
/commitRead
funktionieren auf die gleiche Weise. - Die Methoden
beginRead
/Write
übernehmen als Eingabe Anzahl der zu lesenden bzw. zu schreibenden Nachrichten und gibt einen booleschen Wert zurück, der angibt, Lesen/Schreiben ist möglich. Wenn Lese- oder Schreibzugriff möglich ist, ist dermemTx
Struktur wird mit Basiszeigern gefüllt, die für direkte Zeiger verwendet werden können. Zugriff auf den gemeinsamen Ringzwischenspeicher. - Die Struktur
MemRegion
enthält Details zu einem Speicherblock. einschließlich des Basiszeigers (Basisadresse des Speicherblocks) und der Länge vonT
(Länge des Speicherblocks in Bezug auf das HIDL-definierte Typ der Nachrichtenwarteschlange). - Die Struktur
MemTransaction
enthält zweiMemRegion
-Elemente Structs,first
undsecond
als Lese- oder Schreibvorgang in Der Ringpuffer erfordert unter Umständen einen Wrapping am Anfang der Warteschlange. Dieses würde bedeuten, dass zwei Basiszeiger erforderlich sind, um Daten in den FMQ zu lesen/zu schreiben, Ringpuffer.
So rufen Sie die Basisadresse und die Länge aus einer MemRegion
-Struktur ab:
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
Zum Abrufen von Verweisen auf die ersten und zweiten MemRegion
s innerhalb eines
MemTransaction
-Objekt:
const MemRegion& getFirstRegion(); // get a reference to the first MemRegion const MemRegion& getSecondRegion(); // get a reference to the second MemRegion
Beispiel für einen Schreibvorgang in FMQ mit Zero-Copy-APIs:
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 }
Die folgenden Hilfsmethoden sind ebenfalls Teil von MemTransaction
:
T* getSlot(size_t idx);
Gibt einen Zeiger auf Slotidx
innerhalb derMemRegions
, die zu diesemMemTransaction
gehören -Objekt enthält. Wenn das ObjektMemTransaction
den Speicher darstellt Regionen zum Lesen/Schreiben von n Elementen des Typs T verwendet,idx
liegt zwischen 0 und N-1.bool copyTo(const T* data, size_t startIdx, size_t nMessages = 1);
nMessages
Elemente des Typs T in die Speicherregionen schreiben vom -Objekt beschrieben, ausgehend vom IndexstartIdx
. Diese Methode verwendetmemcpy()
und ist nicht für eine Null-Kopie-Funktion gedacht . Wenn das ObjektMemTransaction
den Arbeitsspeicher N Elemente des Typs T lesen/schreiben, dann ist der gültige Bereich vonidx
zwischen 0 und N-1 liegen.bool copyFrom(T* data, size_t startIdx, size_t nMessages = 1);
Hilfsmethode zum Lesen vonnMessages
Elementen des Typs T aus dem vom Objekt beschriebene Speicherbereiche abstartIdx
. Dieses -Methode verwendetmemcpy()
und ist nicht für eine Nullkopie gedacht .
Warteschlange über HIDL senden
Beim Erstellen:
- Erstellen Sie das Nachrichtenwarteschlangenobjekt wie oben beschrieben.
- Prüfen Sie mit
isValid()
, ob das Objekt gültig ist. - Wenn Sie auf mehrere Warteschlangen warten, indem Sie eine
EventFlag
in die Langform vonreadBlocking()
/writeBlocking()
, können Sie extrahieren: Event-Flag-Zeiger (mithilfe vongetEventFlagWord()
) von einemMessageQueue
-Objekt, das zum Erstellen des Flags initialisiert wurde, und verwenden Sie dieses Flag, um das erforderlicheEventFlag
-Objekt zu erstellen. - Verwenden Sie die
MessageQueue
-MethodegetDesc()
, um eine Deskriptorobjekt. - Geben Sie der Methode in der Datei
.hal
einen Parameter des Typsfmq_sync
oderfmq_unsync
, wobeiT
ein geeigneten HIDL-definierten Typ. Verwenden Sie dies, um das Objekt zu senden, das vongetDesc()
an den Empfangsprozess.
Auf der Empfängerseite:
- Verwenden Sie das Deskriptorobjekt, um ein
MessageQueue
-Objekt zu erstellen. Seien denselben Warteschlangen-Flavor und Datentyp verwenden, da die Vorlage sonst kompilieren. - Wenn Sie ein Ereignis-Flag extrahiert haben, extrahieren Sie das Flag aus der entsprechenden
MessageQueue
-Objekt im Empfangsprozess. - Verwenden Sie das Objekt
MessageQueue
, um Daten zu übertragen.