Android HIDL 官方文档(五)—— 快速消息队列(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 or MQDescriptorUnsync 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 multiple MessageQueue 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 either kSynchronizedReadWrite for a synchronized queue or kUnsynchronizedWrite for an unsynchronized queue.
  • uint16_t (in this example) can be any HIDL-defined type that does not involve nested buffers (no string or vec 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 可以是任何不涉及嵌套的缓冲区(非 stringvec),句柄或接口的 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. The MQDescriptor object is sent over a HIDL RPC call to the process that will hold the second end of the message queue. The MQDescriptor 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 this MessageQueue object. In an unsynchronized queue, the read position (which is local to each MessageQueue object in unsynchronized queues) is always set to 0 during creation. Typically, the MQDescriptor is initialized during creation of the first message queue object. For extra control over the shared memory, you can set up the MQDescriptor manually (MQDescriptor is defined in system/libhidl/base/include/hidl/MQDescriptor.h) then create every MessageQueue object as described in this section.

       参数 resetPointers 表明了在创建这个 MessageQueue 时是否将读取与写入位置重置为 0。对于异步队列,在创建时其读取位置(对于异步队列的每个 MessageQueue 对象都是局部的)都会设置为 0。典型地,在创建第一个消息队列的期间 MQDescriptor 就会被初始化。对于额外的共享内存控制,您可以手动地设置 MQDescriptorMQDescriptor 定义在 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 each readBlocking() and writeBlocking() call. One of the queues may be initialized with an internal event flag, which must then be extracted from that queue’s MessageQueue objects using getEventFlagWord() and used to create EventFlag objects in each process for use with other FMQs. Alternatively, the EventFlag 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() and availableToRead() 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 through availableToRead().

       availableToWrite()availableToRead() 可以确定在单次操作中有多少数据可以被传输。在一个异步队列中:

  • availableToWrite() 总是返回队列的容量。
  • 每个读者都有自己的读取位置,并且对 availableToRead() 进行计算。
  • 从一个较慢的读者的角度来看,队列是允许溢出的,这导致了 availableToRead() 会返回一个大于队列当前数据量大小的值。无论 availableToRead() 是否会报告溢出,溢出后的第一次读操作会失败,并且对应的读者的读取位置会被设置成写入者当前的写入位置。

The read() and write() 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 return true), or return failure (false) immediately.

       当所有需要的数据都可以被传输到队列或从队列传输出来时,read()write() 方法将返回 true。它们不会阻塞,如果处理成功就直接返回 true,失败直接返回 false

The readBlocking() and writeBlocking() methods wait until the requested operation can be completed, or until they timeout (a timeOutNanos 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() and writeBlocking(). 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 calling getEventFlagWord(), and that pointer (or any pointer to a suitable shared memory location) can be used to create an EventFlag object to pass into the long form of readBlocking() and writeBlocking() for a different queue. The readNotification and writeNotification parameters tell which bits in the event flag should be used to signal reads and writes on that queue. readNotification and writeNotification are 32-bit bitmasks.

       阻塞操作是通过一个事件标志词来实现的。默认情况下,每个队列自行创建并使用标志词提供给简易格式的 readBlocking()writeBlocking()。多个队列可以共享一个单独的标志词,如此一来进程就可以在写或读操作时等待。通过调用 getEventFlagWord(),可以获得队列事件标记词的指针,而这个指针(或者任何指向合适的共享内存位置的指针)可以用于创建一个 EventFlag 对象,以传递到另一个不同队列的详细格式的 readBlocking()writeBlocking() 方法中。参数 readNotificationwriteNotification 说明了对于相应的队列,事件标志词中的哪一位是用于通知读操作与写操作的。readNotificationwriteNotification 是 32 位的位掩码。

readBlocking() waits on the writeNotification bits; if that parameter is 0, the call always fails. If the readNotification 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 corresponding writeBlocking() 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 if readNotification is 0, and a successful write sets the specified writeNotification bits.

       readBlocking() 等待于 writeNotification 位,如果该参数为 0,则对该函数的调用总是失败的。但如果 readNotification 也为 0,则调用就不会失败,不过读取操作成功时不会设置相应的通知位。在同步队列中,相应的 writeBlocking() 将一直不会被唤醒,除非在别处设置了标志位。在异步队列中,writeBlocking() 不会等待(它仍应被用于设置写通知位),并且对于读操作不设置任何通知位是合适的。类似地,readNotification0 时,writeblocking() 将会失败,并且一次成功的写操作将会设置指定的 writeNotification 位。

To wait on multiple queues at once, use an EventFlag object’s wait() 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 nonblocking read()/write() followed by a call to the EventFlag’s wake() method if a notification is desired after the same. For a definition of the EventFlag abstraction, refer to system/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 use memcpy() 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 use memcpy 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 using commitWrite(). 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 the read/write is possible. If the read or write is possible the memTx 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 two MemRegion structs, first and second 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 结构,firstsecond 。读入或写入循环缓冲区时,可能需要绕到队列的开头,这就意味着需要两个基础指针将数据从 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 the MemRegions that are part of this MemTransaction object. If the MemTransaction object is representing the memory regions to read/write N items of type T, then the valid range of idx 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 index startIdx. This method uses memcpy() and is not to meant to be used for a zero copy operation. If the MemTransaction 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 from startIdx. This method uses memcpy() 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:

  1. Create message queue object as described above.
  2. Verify the object is valid with isValid().
  3. If you will be waiting on multiple queues by passing an EventFlag into the long form of readBlocking()/writeBlocking(), you can extract the event flag pointer (using getEventFlagWord()) from a MessageQueue object that was initialized to create the flag, and use that flag to create the necessary EventFlag object.
  4. Use the MessageQueue getDesc() method to get a descriptor object.
  5. In the .hal file, give a method a parameter of type fmq_sync or fmq_unsync where T is a suitable HIDL-defined type. Use this to send the object returned by getDesc() to the receiving process.

       在创建的方面:

  • 创建如上所述的消息队列对象。
  • 调用 isValid() 验证对象是否有效。
  • 若您需要通过将 EventFlag 传递到详细格式的 readBlocking() / writeBlocking() 方法中以等待多个队列,您可以从在初始化时创建了事件标志的 MessageQueue 对象中,通过调用 getEventFlagWord() 方法获取事件标志指针,并通过这个标志创建一个必要的 EventFlag 对象。
  • 使用 MessageQueue getDesc() 获取一个描述符对象。
  • .hal 文件中,给出一个 fmq_syncfmq_unsync 的参数的方法,其中 T 是一个适当的 HIDL-defined 类型。使用这个方法将 getDesc() 所返回的对象发送到接收消息的进程中。

On the receiving side:

  1. Use the descriptor object to create a MessageQueue object. Be sure to use the same queue flavor and data type, or the template will fail to compile.
  2. If you extracted an event flag, extract the flag from the corresponding MessageQueue object in the receiving process.
  3. Use the MessageQueue object to transfer data.

       对于接收方:

  • 采用收到的描述符对象创建一个 MessageQueue 对象,需要保证使用相同的队列 flavor 与数据类型,否则模板将无法编译。
  • 若您提取了事件标志,则需要从接收消息的进程中对应的 MessageQueue 对象中提取相应的标志。
  • 使用 MessageQueue 对象传输数据。
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值