- 对应的官方文档地址:HIDL(General) - Fast Message Queue (FMQ)
HIDL’s remote procedure call (RPC) infrastructure uses Binder mechanisms, meaning calls involve overhead, require kernel operations, and may trigger scheduler action. However, for cases where data must be transferred between processes with less overhead and no kernel involvement, the Fast Message Queue (FMQ) system is used.
HIDL 的远程过程调用(remote procedure call,RPC)的基础是使用 Binder 机制。这就意味着调用涉及到开销,需要内核操作,并且还可能会触发调度器操作。然而,如果数据必须在开销比较少,并且没有内核参与进程间传输的情况下,就需要使用到快速消息队列(Fast Message Queue,FMQ)系统。
FMQ creates message queues with the desired properties. An
MQDescriptorSync
orMQDescriptorUnsync
object can be sent over a HIDL RPC call and used by the receiving process to access the message queue.
FMQ根据所需的属性创建消息队列。通过 HIDL RPC 调用,可以发送 MQDescriptorSync
或者 MQDescriptorUnsync
对象,并通过接收进程访问消息队列去使用它们。
Fast Message Queues are supported only in C++.
需要注意的是,只有 C++ 支持快速消息队列系统。
1. 消息队列类型
(MessageQueue types)
Android supports two queue types:
- Unsynchronized queues are allowed to overflow, and can have many readers; each reader must read data in time or lose it.
- Synchronized queues are not allowed to overflow, and can have only one reader.
Android 系统支持两种队列类型:
- 异步队列是允许溢出的,并且可以有多个读者(readers),每个读者都需要及时地获取或丢失数据。
- 同步队列是不允许溢出的,同时它只能有一个读者。
Both queue types are not allowed to underflow (read from an empty queue will fail) and can only have one writer.
两种队列都不可下溢(读取一个空队列会导致失败),并且只能有一个写入者(writer)。
1.1 异步
(Unsynchronized)
An unsynchronized queue has only one writer, but can have any number of readers. There is one write position for the queue; however, each reader keeps track of its own independent read position.
一个异步队列只有一个写入者,但可以拥有多个读者。队列中只有一个写入位置,但是每个读者会跟踪自己独立的读取位置。
Writes to the queue always succeed (are not checked for overflow) as long as they are no larger than the configured queue capacity (writes larger than the queue capacity fail immediately). As each reader may have a different read position, rather than waiting for every reader to read every piece of data, data is allowed to fall off the queue whenever new writes need the space.
写入队列总是成功的(不检查溢出),只要他们不大于所配置的队列容量(如果写入的数据大于容量,则会直接失败)。由于每个读者对应不同的读取位置,而不是等待每个读者读取每一条数据,所以只要有新的数据写入空间,数据就被允许从队列上掉出(fall off)。
Reads are responsible for retrieving data before it falls off the end of the queue. A read that attempts to read more data than is available either fails immediately (if nonblocking) or waits for enough data to be available (if blocking). A read that attempts to read more data than the queue capacity always fails immediately.
读取操作负责在数据掉出队列末端前对其进行检索。当一次读取操作尝试读取大于可获得的数据量时,要么就会直接失败(非阻塞情况),要么就等待足量的可获取数据加入(阻塞情况)。如果读取操作尝试一次读取超过队列容量的数据,那当然是要直接返回失败的。
If a reader fails to keep up with the writer, so that the amount of data written and not yet read by that reader is larger than the queue capacity, the next read does not return data; instead, it resets the readers read position to equal the latest write position then returns failure. If the data available to read is checked after overflow but before the next read, it shows more data available to read than the queue capacity, indicating overflow has occurred. (If the queue overflows between checking available data and attempting to read that data, the only indication of overflow is that the read fails.)
如果一个读者没有跟上写入者的步伐,导致要写入的数据与还未读取的数据大于队列容量,则下一次的读取就不会返回数据。相反地,它会将读者的读取位置重置到最新的写入位置,并且返回失败信息。若读取的数据在溢出后,但在下一次读取之前被检查,它显示出比队列容量更多的可用数据,则表明溢出已经发生(如果在检查可用数据与试图读取数据之间队列溢出,则溢出唯一标志便是读取失败)。
1.2 同步
(Synchronized)
A synchronized queue has one writer and one reader with a single write position and a single read position. It is impossible to write more data than the queue has space for or read more data than the queue currently holds. Depending on whether the blocking or nonblocking write or read function is called, attempts to exceed available space or data either return failure immediately or block until the desired operation can be completed. Attempts to read or write more data than the queue capacity will always fail immediately.
一个同步队列只有一个写入者和一个读者,这也对应了单独的写入位置与读取位置。它不可能被写入比其容量更多的数据,或者读取比当前保存的数据更多的数据量。操作的返回结果取决于函数的调用是阻塞的或非阻塞的,当进行超过可用空间或数据的尝试时,要么立即返回失败,要么阻塞直到完成所需的操作为止。尝试超过队列容量的读写操作会直接返回失败。
2. 设置一个快速消息队列
(Setting up an FMQ)
A message queue requires multipleMessageQueue
objects: one to be written to, and one or more to be read from. There is no explicit configuration of which object is used for writing or reading; it is up to the user to ensure that no object is used for both reading and writing, that there is at most one writer, and, for synchronized queues, that there is at most one reader.
一个消息队列需要多个 MessageQueue
对象:一个用于写入,其它用于读取。没有明确的配置说明对象是用于读还是写,用户必须自己保证一个对象不能同时用于读和写。同时,最多只有一个写入者,对于同步的情况,最多只有一个读者。
2.1 创建第一个消息队列对象
(Creating the first MessageQueue object)
A message queue is created and configured with a single call:
创建并配置一个消息队列:
#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 */);
- The
MessageQueue<T, flavor>(numElements)
initializer creates and initializes an object that supports the message queue functionality.- The
MessageQueue<T, flavor>(numElements, configureEventFlagWord)
initializer creates and initializes an object that supports the message queue functionality with blocking.flavor
can be eitherkSynchronizedReadWrite
for a synchronized queue orkUnsynchronizedWrite
for an unsynchronized queue.uint16_t (in this example)
can be any HIDL-defined type that does not involve nested buffers (nostring
orvec
types), handles, or interfaces.kNumElementsInQueue
indicates the size of queue in number of entries; it determines the size of shared memory buffer that will be allocated for the queue.
- 初始化器(构造函数)
MessageQueue<T, flavor>(numElements)
创建并初始化一个支持消息队列功能的对象。 - 构造函数
MessageQueue<T, flavor>(numElements, configureEventFlagWord)
创建并初始化一个可阻塞的支持消息队列功能的对象。 flavor
可以是同步队列对应的kSynchronizedReadWrite
,也可以是异步队列对应的kUnsynchronizedWrite
。- 本例中,
uint16_t
可以是任何不涉及嵌套的缓冲区(非string
或vec
),句柄或接口的 HIDL-defines 的数据类型。 kNumElementsInQueue
表明了在入口数中的队列大小,它决定了为队列申请的共享内存缓冲区的大小。
2.2 创建第二个消息队列对象
(Creating the second MessageQueue object)
The second side of the message queue is created using an
MQDescriptor
object obtained from the first side. TheMQDescriptor
object is sent over a HIDL RPC call to the process that will hold the second end of the message queue. TheMQDescriptor
contains information about the queue:
- Information to map the buffer and write pointer.
- If the queue is synchronized, information to map the read pointer.
- If the queue is blocking, information to map the event flag word.
- The object type is made into a template with the HIDL-defined type of queue elements and the flavor (synchronized or unsynchronized).
消息队列的第二端是使用从第一端所获得的 MQDescriptor
对象创建的。MQDescriptor
对象是通过 HIDL RPC 调用发送到即将保存消息队列的第二端进程的。MQDescriptor
包含以下几种队列信息:
- 用于映射缓冲区和写入指针的信息。
- 如果队列是同步的,还有映射读者指针的信息。
- 如果队列是阻塞的,还有映射事件标志词的信息。
- 对象类型被做成一个带有 HIDL-defines 的队列元素与特点(同步或异步)的模板。
The MQDescriptor object can be used to construct a MessageQueue object:
MQDescriptor
对象可以被用于构建一个 MessageQueue
对象:
MessageQueue<T, flavor>::MessageQueue(const MQDescriptor<T, flavor>& Desc, bool resetPointers)
The
resetPointers
parameter indicates whether to reset the read and write positions to 0 while creating thisMessageQueue
object. In an unsynchronized queue, the read position (which is local to eachMessageQueue
object in unsynchronized queues) is always set to 0 during creation. Typically, theMQDescriptor
is initialized during creation of the first message queue object. For extra control over the shared memory, you can set up theMQDescriptor
manually (MQDescriptor
is defined insystem/libhidl/base/include/hidl/MQDescriptor.h
) then create everyMessageQueue
object as described in this section.
参数 resetPointers
表明了在创建这个 MessageQueue
时是否将读取与写入位置重置为 0
。对于异步队列,在创建时其读取位置(对于异步队列的每个 MessageQueue
对象都是局部的)都会设置为 0
。典型地,在创建第一个消息队列的期间 MQDescriptor
就会被初始化。对于额外的共享内存控制,您可以手动地设置 MQDescriptor
(MQDescriptor
定义在 system/libhidl/base/include/hidl/MQDescriptor.h
)然后创建本节描述中的每个 MessageQueue
对象。
2.3 阻塞队列与事件标志
(Blocking queues and event flags)
By default, queues do not support blocking reads/writes. There are two kinds of blocking read/write calls:
- Short form, with three parameters (data pointer, number of items, timeout). Supports blocking on individual read/write operations on a single queue. When using this form, the queue will handle the event flag and bitmasks internally, and the first message queue object must be initialized with a second parameter of true.
- Long form, with six parameters (includes event flag and bitmasks). Supports using a shared EventFlag object between multiple queues and allows specifying the notification bit masks to be used. In this case, the event flag and bitmasks must be supplied to each read and write call.
在默认情况下,队列是不支持阻塞的读写操作的,以下列出两种阻塞的读写调用方式:
- 简易格式,带有三个参数(数据指针,项的个数,超时)。支持单个队列上的独立的阻塞读写操作。使用这种格式时,队列将在内部处理事件标志与位掩码,同时第一个消息队列初始化时的第二个参数要设置为
true
。 - 详细格式,带有六个参数(包括事件标志与位掩码)。支持使用一个在多个队列间共享的
EventFlag
对象,以及允许指定要使用的通知位掩码。在这种情况下,事件标志与位掩码要提供给每个读写调用。
For the long form, the
EventFlag
can be supplied explicitly in eachreadBlocking()
andwriteBlocking()
call. One of the queues may be initialized with an internal event flag, which must then be extracted from that queue’sMessageQueue
objects usinggetEventFlagWord()
and used to createEventFlag
objects in each process for use with other FMQs. Alternatively, theEventFlag
objects can be initialized with any suitable shared memory.
对于详细格式,EventFlag
对象可由每个 readBlocking()
与 writeBlocking()
显式地调用。其中一个队列可以使用内部的事件标志进行初始化,它必须使用 getEventFlagWord()
从该队列的 MessageQueue
对象中提取出来,并且勇于在每个进程中创建 EventFlag
对象,以便其它快速消息队列一起使用。或者,EventFlag
对象可以用于任何合适的共享内存的初始化。
In general, each queue should use only one of non-blocking, short-form blocking, or long-form blocking. It is not an error to mix them, but careful programming is required to get the desired result.
通常一个队列只应使用非阻塞,简易格式阻塞与详细格式阻塞这三种方式的其中一种。将它们混合使用不是错误的情况,但需要非常仔细地编程才能获得所需要的结果。
3. 使用消息队列
(Using the MessageQueue)
The public API of the
MessageQueue
object is:
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()
andavailableToRead()
can be used to determine how much data can be transferred in a single operation. In an unsynchronized queue:
availableToWrite()
always returns the capacity of the queue.- Each reader has its own read position and does its own calculation for
availableToRead()
.- From the point of view of a slow reader, the queue is allowed to overflow; this may result in
availableToRead()
returning a value larger than the size of the queue. The first read after an overflow will fail and result in the read position for that reader being set equal to the current write pointer, whether or not the overflow was reported throughavailableToRead()
.
availableToWrite()
与 availableToRead()
可以确定在单次操作中有多少数据可以被传输。在一个异步队列中:
availableToWrite()
总是返回队列的容量。- 每个读者都有自己的读取位置,并且对
availableToRead()
进行计算。 - 从一个较慢的读者的角度来看,队列是允许溢出的,这导致了
availableToRead()
会返回一个大于队列当前数据量大小的值。无论availableToRead()
是否会报告溢出,溢出后的第一次读操作会失败,并且对应的读者的读取位置会被设置成写入者当前的写入位置。
The
read()
andwrite()
methods return true if all requested data could be (and was) transferred to/from the queue. These methods do not block; they either succeed (and returntrue
), or return failure (false
) immediately.
当所有需要的数据都可以被传输到队列或从队列传输出来时,read()
和 write()
方法将返回 true
。它们不会阻塞,如果处理成功就直接返回 true
,失败直接返回 false
。
The
readBlocking()
andwriteBlocking()
methods wait until the requested operation can be completed, or until they timeout (atimeOutNanos
value of 0 means never timeout).
readBlocking()
与 writeBlocking()
方法将会等待直到操作完成为止,或者等到超时(timeOutNanos
值为 0 则表示不设置超时限制)后返回失败。
Blocking operations are implemented using an event flag word. By default, each queue creates and uses its own flag word to support the short form of
readBlocking()
andwriteBlocking()
. It is possible for multiple queues to share a single word, so that a process can wait on writes or reads to any of the queues. A pointer to a queue’s event flag word can be obtained by callinggetEventFlagWord()
, and that pointer (or any pointer to a suitable shared memory location) can be used to create anEventFlag
object to pass into the long form ofreadBlocking()
andwriteBlocking()
for a different queue. ThereadNotification
andwriteNotification
parameters tell which bits in the event flag should be used to signal reads and writes on that queue. readNotification
andwriteNotification
are 32-bit bitmasks.
阻塞操作是通过一个事件标志词来实现的。默认情况下,每个队列自行创建并使用标志词提供给简易格式的 readBlocking()
与 writeBlocking()
。多个队列可以共享一个单独的标志词,如此一来进程就可以在写或读操作时等待。通过调用 getEventFlagWord()
,可以获得队列事件标记词的指针,而这个指针(或者任何指向合适的共享内存位置的指针)可以用于创建一个 EventFlag
对象,以传递到另一个不同队列的详细格式的 readBlocking()
和 writeBlocking()
方法中。参数 readNotification
与 writeNotification
说明了对于相应的队列,事件标志词中的哪一位是用于通知读操作与写操作的。readNotification
与 writeNotification
是 32 位的位掩码。
readBlocking()
waits on thewriteNotification
bits; if that parameter is 0, the call always fails. If thereadNotification
value is 0, the call will not fail, but a successful read will not set any notification bits. In a synchronized queue, this would mean that the correspondingwriteBlocking()
call will never wake up unless the bit is set elsewhere. In an unsynchronized queue,writeBlocking()
will not wait (it should still be used to set the write notification bit), and it is appropriate for reads to not set any notification bits. Similarly,writeblocking()
will fail ifreadNotification
is 0, and a successful write sets the specifiedwriteNotification
bits.
readBlocking()
等待于 writeNotification
位,如果该参数为 0
,则对该函数的调用总是失败的。但如果 readNotification
也为 0
,则调用就不会失败,不过读取操作成功时不会设置相应的通知位。在同步队列中,相应的 writeBlocking()
将一直不会被唤醒,除非在别处设置了标志位。在异步队列中,writeBlocking()
不会等待(它仍应被用于设置写通知位),并且对于读操作不设置任何通知位是合适的。类似地,readNotification
为 0
时,writeblocking()
将会失败,并且一次成功的写操作将会设置指定的 writeNotification
位。
To wait on multiple queues at once, use an
EventFlag
object’swait()
method to wait on a bitmask of notifications. The wait() method returns a status word with the bits that caused the wake up set. Using the information, the user can then check the corresponding queue to see whether it has enough space or data for the desired write or read operation and perform a nonblockingread()/write()
followed by a call to theEventFlag
’s wake() method if a notification is desired after the same. For a definition of theEventFlag
abstraction, refer tosystem/libfmq/include/fmq/EventFlag.h
.
为了同时等待多个队列,使用一个 EventFlag
对象的 wait()
方法等待相应的位掩码的通知。wait()
方法会返回一个用位引起唤醒操作的状态字。通过使用这个信息,用户可以检查相应的队列,看看它是否拥有足够的空间或者数据用于所需的写入或读取操作,并且执行一个非阻塞的 read()
/ write()
后,如果在相同情况下需要得到一个通知,则会调用 EventFlag
对象的 wake()
方法。对于 EventFlag
的抽象定义,参见 system/libfmq/include/fmq/EventFlag.h
。
4. 零拷贝操作
(Zero copy operations)
The
read/write/readBlocking/writeBlocking()
APIs take a pointer to an input/output buffer as an argument and usememcpy()
calls internally to copy data between the same and the FMQ ring buffer. To improve performance, Android O includes a set of APIs that provide direct pointer access into the ring buffer, eliminating the need to usememcpy
calls.Use the following public APIs for zero copy FMQ operations:
read
/ write
/ readBlocking
/ writeBlocking()
这几个接口都需要将一个指向输入或输出缓冲区的指针作为参数,并且通过内部调用 memcpy()
在相同的 FMQ 循环缓冲区之间复制数据。为了提升性能,Android O 包含了一个 API 集合,它们提供一个访问循环缓冲区的直接指针,消除了对 memcpy()
的调用需要。
以下几个公开的 API 都是零拷贝的操作:
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);
- The
beginWrite
method provides base pointers into the FMQ ring buffer. After the data is written, commit it usingcommitWrite()
.ThebeginRead/commitRead
methods act the same way.- The
beginRead/Write
methods take as input the number of messages to be read/written and return a boolean indicating if theread/write
is possible. If the read or write is possible thememTx
struct is populated with base pointers that can be used for direct pointer access into the ring buffer shared memory.- The
MemRegion
struct contains details about a block of memory, i.e. a base pointer and length in terms of T(where the FMQ is templatized to T).- The
MemTransaction
struct contains twoMemRegion
structs,first
andsecond
as a read or write into the ring buffer may require a wrap around to the beginning of the queue. This would mean that two base pointers are needed to read/write data into the FMQ ring buffer.
beginWrite
方法提供了指向 FMQ 循环缓冲区的基础指针。数据被写入后,通过调用commitWrite()
来提交数据。beginRead
/commitRead
方法也是相同的业务逻辑。beginRead
/Write
方法将消息的数量作为输入,用以读写,并且返回一个布尔值,标志着读取或写入操作是否可行。如果是可行的,就通过基础指针填充memTx
数据结构,这个结构可用于令直接指针访问到循环缓冲区的共享内存。MemRegion
结构则包含了一块内存空间的详细信息。比如一个基础指针与T
(FMQ 的模板类型T
) 的长度。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
从 MemTransaction
对象中获取第一与第二 MemRegion
引用:
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
}
The following helper methods are also part of MemTransaction:
T* getSlot(size_t idx);
- Returns a pointer to slot
idx
within theMemRegions
that are part of thisMemTransaction
object. If theMemTransaction
object is representing the memory regions to read/write N items of type T, then the valid range ofidx
is between 0 and N-1.bool copyTo(const T* data, size_t startIdx, size_t nMessages = 1);
- Write
nMessages
’ items of type T into the memory regions described by the object, starting from indexstartIdx
. This method usesmemcpy()
and is not to meant to be used for a zero copy operation. If theMemTransaction
object represents memory to read/write N items of type T, then the valid range of idx is between 0 and N-1.bool copyFrom(T* data, size_t startIdx, size_t nMessages = 1);
- Helper method to read
nMessages
’ items of type T from the memory regions described by the object starting fromstartIdx
. This method usesmemcpy()
and is not meant to be used for a zero copy operation.
以下几个辅助方法也是 MemTransaction
的一部分:
T* getSlot(size_t idx)
:
- 返回一个指向
memRegion
内的槽idx
的指针,这是该MemTransaction
对象的一部分。若该MemTransaction
对象表示在内存区域读或写T
类型的N
个项,则idx
的有效范围在0 ~ (N - 1)
之间。
- 返回一个指向
bool copyTo(const T* data, size_t startIdx, size_t nMessages = 1)
:
- 写入
T
类型的项nMessages
到该对象对应的内存区域,从下标startIdx
开始。这个方法使用了memcpy()
,它并不是用于实现零拷贝操作的。如果MemTransaction
对象表示内存读或写T
类型的N
个项,则idx
有效分为在0 ~ (N - 1)
之间。
- 写入
bool copyFrom(T* data, size_t startIdx, size_t nMessages = 1)
:
- 从对象所描述的区域中读取
T
类型的项nMessages
的辅助方法,从下标startIdx
开始。这个方法也使用了memcpy()
,它并不是用于实现零拷贝操作的。
- 从对象所描述的区域中读取
5. 将队列发送到 HIDL
(Sending the queue over HIDL)
On the creating side:
- Create message queue object as described above.
- Verify the object is valid with
isValid()
.- If you will be waiting on multiple queues by passing an
EventFlag
into the long form ofreadBlocking()/writeBlocking()
, you can extract the event flag pointer (usinggetEventFlagWord()
) from aMessageQueue
object that was initialized to create the flag, and use that flag to create the necessaryEventFlag
object.- Use the
MessageQueue
getDesc()
method to get a descriptor object.- In the
.hal
file, give a method a parameter of typefmq_sync
orfmq_unsync
where T is a suitable HIDL-defined type. Use this to send the object returned bygetDesc()
to the receiving process.
在创建的方面:
- 创建如上所述的消息队列对象。
- 调用
isValid()
验证对象是否有效。 - 若您需要通过将
EventFlag
传递到详细格式的readBlocking()
/writeBlocking()
方法中以等待多个队列,您可以从在初始化时创建了事件标志的MessageQueue
对象中,通过调用getEventFlagWord()
方法获取事件标志指针,并通过这个标志创建一个必要的EventFlag
对象。 - 使用
MessageQueue getDesc()
获取一个描述符对象。 - 在
.hal
文件中,给出一个fmq_sync
或fmq_unsync
的参数的方法,其中T
是一个适当的 HIDL-defined 类型。使用这个方法将getDesc()
所返回的对象发送到接收消息的进程中。
On the receiving side:
- Use the descriptor object to create a
MessageQueue
object. Be sure to use the same queueflavor
and data type, or the template will fail to compile.- If you extracted an event flag, extract the flag from the corresponding
MessageQueue
object in the receiving process.- Use the
MessageQueue
object to transfer data.
对于接收方:
- 采用收到的描述符对象创建一个
MessageQueue
对象,需要保证使用相同的队列flavor
与数据类型,否则模板将无法编译。 - 若您提取了事件标志,则需要从接收消息的进程中对应的
MessageQueue
对象中提取相应的标志。 - 使用
MessageQueue
对象传输数据。