Jika Anda mencari dukungan AIDL, lihat juga FMQ dengan AIDL.
Infrastruktur remote procedure call (RPC) HIDL menggunakan mekanisme Binder, yang berarti panggilan melibatkan overhead, memerlukan operasi kernel, dan dapat memicu tindakan penjadwal. Namun, untuk kasus di mana data harus ditransfer antara proses dengan lebih sedikit overhead dan tanpa keterlibatan {i> kernel<i}, Antrean Pesan Cepat (FMQ) digunakan.
FMQ membuat antrean pesan dengan properti yang diinginkan. Channel
Objek MQDescriptorSync
atau MQDescriptorUnsync
dapat
dikirim melalui panggilan HIDL RPC dan digunakan oleh proses penerima untuk mengakses
antrean pesan.
Antrean Pesan Cepat hanya didukung di C++ dan di perangkat menjalankan Android 8.0 dan yang lebih tinggi.
Jenis MessageQueue
Android mendukung dua jenis antrean (disebut sebagai ragam):
- Antrean yang tidak disinkronkan diizinkan untuk melebihi batas, dan dapat memiliki banyak pembaca; setiap pembaca harus membaca data tepat waktu atau kehilangan data.
- Antrean yang disinkronkan tidak boleh melebihi batas, dan hanya boleh memiliki satu pembaca.
Kedua jenis antrean tidak diizinkan untuk underflow (dibaca dari antrean kosong gagal) dan hanya dapat memiliki satu penulis.
Tidak disinkronkan
Sebuah antrean yang tidak tersinkronisasi hanya memiliki satu penulis, tetapi dapat memiliki pembaca. Ada satu posisi tulis untuk antrean; Namun, setiap pembaca menyimpan melacak posisi baca independennya.
Penulisan ke antrean selalu berhasil (tidak diperiksa apakah ada kelebihan) selama mereka tidak lebih besar dari kapasitas antrian yang terkonfigurasi (penulisan lebih besar dari kapasitas antrean akan langsung gagal). Karena setiap pembaca mungkin memiliki daripada menunggu setiap pembaca untuk membaca setiap bagian data, diizinkan untuk keluar dari antrean setiap kali operasi tulis baru memerlukan ruang.
Pembaca bertanggung jawab untuk mengambil data sebelum data itu jatuh pada akhir antrean. Operasi baca yang mencoba membaca lebih banyak data daripada yang tersedia gagal segera (jika tidak memblokir) atau menunggu cukup data tersedia (jika pemblokiran). Operasi baca yang selalu berupaya membaca lebih banyak data daripada kapasitas antrean gagal.
Jika pembaca gagal mengikuti perkembangan penulis, sehingga jumlah data yang ditulis dan belum dibaca oleh pembaca tersebut lebih besar dari kapasitas antrian, {i>next read<i} tidak mengembalikan data; sebagai gantinya, tindakan ini akan mereset untuk menyamakan posisi tulis terbaru lalu mengembalikan kegagalan. Jika data yang tersedia untuk dibaca diperiksa setelah {i> overflow<i} tetapi sebelum pembacaan berikutnya, menunjukkan lebih banyak data yang tersedia untuk dibaca daripada kapasitas antrean, menunjukkan terjadi tambahan. (Jika antrean melebihi pemeriksaan data yang tersedia dan mencoba membaca data tersebut, satu-satunya indikasi {i>overflow<i} adalah pembacaan gagal.)
Pembaca dari antrean yang tidak disinkronkan kemungkinan tidak ingin direset pointer baca dan tulis dari antrean. Jadi, saat membuat antrean dari pembaca deskriptor harus menggunakan argumen `false` untuk `resetPointers` .
Disinkronkan
Antrean yang disinkronkan memiliki satu penulis dan satu pembaca dengan satu penulisan satu posisi dan satu posisi baca. Tidak mungkin untuk menulis lebih banyak data dari antrean memiliki ruang untuk atau membaca lebih banyak data daripada antrean yang saat ini ada. Tergantung pada apakah fungsi operasi tulis atau baca yang memblokir atau tidak memblokir yang dipanggil, mencoba untuk melebihi ruang yang tersedia atau data yang gagal mengembalikan segera atau memblokir sampai operasi yang diinginkan dapat diselesaikan. Upaya untuk membaca atau menulis lebih banyak data daripada kapasitas antrean selalu gagal.
Menyiapkan FMQ
Antrean pesan memerlukan beberapa objek MessageQueue
: satu hingga
ditulis, dan satu atau
lebih untuk dibaca. Tidak ada eksplisit
konfigurasi objek mana yang digunakan untuk menulis atau membaca; terserah
untuk memastikan bahwa tidak ada objek yang
digunakan untuk membaca dan menulis, yang
hanya ada satu penulis, dan untuk antrean yang disinkronkan, setidaknya ada satu
pembaca.
Membuat objek MessageQueue pertama
Antrean pesan dibuat dan dikonfigurasi dengan satu panggilan:
#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 */);
- Penginisialisasi
MessageQueue<T, flavor>(numElements)
membuat dan melakukan inisialisasi objek yang mendukung fungsionalitas antrean pesan. - Penginisialisasi
MessageQueue<T, flavor>(numElements, configureEventFlagWord)
membuat dan melakukan inisialisasi objek yang mendukung fungsionalitas antrean pesan dengan pemblokiran. flavor
dapat berupakSynchronizedReadWrite
untuk antrean tersinkronisasi ataukUnsynchronizedWrite
untuk antrean.uint16_t
(dalam contoh ini) dapat berupa Jenis yang didefinisikan HiDL yang tidak melibatkan buffer bertingkat (tanpastring
atauvec
{i>handle<i}, {i>handle<i}, atau antarmuka.kNumElementsInQueue
menunjukkan ukuran antrean dalam jumlah entri; menentukan ukuran {i>buffer<i} memori bersama yang dialokasikan untuk antrean.
Membuat objek MessageQueue kedua
Sisi kedua antrean pesan dibuat menggunakan
Objek MQDescriptor
yang diperoleh dari sisi pertama. Tujuan
Objek MQDescriptor
dikirim melalui panggilan HIDL atau AIDL RPC ke proses
yang menampung ujung kedua antrean pesan. Tujuan
MQDescriptor
berisi informasi tentang antrean, termasuk:
- Informasi untuk memetakan buffer dan menulis pointer.
- Informasi untuk memetakan pointer baca (jika antrean disinkronkan).
- Informasi untuk memetakan kata flag peristiwa (jika antrean memblokir).
- Jenis objek (
<T, flavor>
), yang mencakup Jenis yang didefinisikan HIDL dari elemen antrean dan ragam antrean (disinkronkan atau tidak disinkronkan).
Objek MQDescriptor
dapat digunakan untuk membuat
Objek MessageQueue
:
MessageQueue<T, flavor>::MessageQueue(const MQDescriptor<T, flavor>& Desc, bool resetPointers)
Parameter resetPointers
menunjukkan apakah akan mereset pembacaan
dan tulis posisi ke 0 saat membuat objek MessageQueue
ini.
Dalam antrean yang tidak tersinkronisasi, posisi baca (yang bersifat lokal
Objek MessageQueue
dalam antrean yang tidak disinkronkan) selalu ditetapkan ke 0
selama pembuatan. Biasanya, MQDescriptor
diinisialisasi selama
pembuatan objek antrean pesan pertama. Untuk kontrol tambahan terhadap
Anda dapat menyiapkan MQDescriptor
secara manual
(MQDescriptor
ditentukan di
system/libhidl/base/include/hidl/MQDescriptor.h
)
lalu buat setiap objek MessageQueue
seperti yang dijelaskan di bagian ini.
Antrean blok dan tanda peristiwa
Secara default, antrean tidak mendukung pemblokiran baca/tulis. Ada dua jenis memblokir panggilan baca/tulis:
- Format pendek, dengan tiga parameter (pointer data, jumlah item,
waktu tunggu). Mendukung pemblokiran pada setiap operasi baca/tulis pada satu
antrean. Saat menggunakan formulir ini, antrean menangani flag peristiwa dan bitmask
secara internal, dan objek antrean pesan pertama harus
diinisialisasi dengan parameter kedua
true
. Contoh:// For an unsynchronized FMQ that supports blocking mFmqUnsynchronizedBlocking = new (std::nothrow) MessageQueue<uint16_t, kUnsynchronizedWrite> (kNumElementsInQueue, true /* enable blocking operations */);
- Format panjang, dengan enam parameter (termasuk flag peristiwa dan bitmask).
Mendukung penggunaan objek
EventFlag
bersama di antara beberapa antrean dan memungkinkan penentuan bit mask notifikasi yang akan digunakan. Dalam hal ini, {i>event flag<i} dan bitmask harus disediakan untuk setiap panggilan baca dan tulis.
Untuk format panjang, EventFlag
dapat diberikan secara eksplisit di
setiap panggilan readBlocking()
dan writeBlocking()
. Salah satu
antrean dapat diinisialisasi dengan penanda
peristiwa internal, yang kemudian harus
diekstrak dari objek MessageQueue
antrean tersebut menggunakan
getEventFlagWord()
dan digunakan untuk membuat EventFlag
objek dalam setiap proses untuk
digunakan dengan FMQ lain. Atau,
Objek EventFlag
dapat diinisialisasi dengan objek bersama yang sesuai
memori.
Secara umum, setiap antrean sebaiknya hanya menggunakan satu format pendek, pemblokiran, atau pemblokiran video panjang. Jangan mencampurnya, tetapi hati-hati pemrograman diperlukan untuk mendapatkan hasil yang diinginkan.
Menandai memori sebagai hanya baca
Secara default, memori bersama memiliki izin baca dan tulis. Untuk yang tidak tersinkronisasi
antrean (kUnsynchronizedWrite
), penulis mungkin ingin menghapus izin tulis untuk semua
pembaca sebelum membagikan objek MQDescriptorUnsync
. Hal ini memastikan bahwa
proses tidak dapat menulis ke antrean, yang disarankan untuk
melindungi data dari {i>bug<i} atau perilaku buruk dalam
diproses oleh pembaca.
Jika penulis ingin pembaca dapat mereset antrean setiap kali mereka menggunakan
MQDescriptorUnsync
untuk membuat sisi baca antrean, lalu memori tidak dapat ditandai
sebagai hanya-baca. Ini adalah perilaku default konstruktor `MessageQueue`. Jadi, jika sudah ada
pengguna yang ada dari antrian ini, kode mereka perlu diubah
untuk menyusun antrian dengan
resetPointer=false
.
- Penulis: panggil
ashmem_set_prot_region
dengan deskriptor fileMQDescriptor
dan region yang disetel ke hanya baca (PROT_READ
):int res = ashmem_set_prot_region(mqDesc->handle->data[0], PROT_READ)
- Pembaca: buat antrean pesan dengan
resetPointer=false
( defaultnya adalahtrue
):mFmq = new (std::nothrow) MessageQueue(mqDesc, false);
Menggunakan MessageQueue
API publik objek MessageQueue
adalah:
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()
dan availableToRead()
dapat digunakan
untuk menentukan berapa banyak data yang
dapat ditransfer dalam satu operasi. Di
antrean yang tidak tersinkronisasi:
availableToWrite()
selalu menampilkan kapasitas antrean.- Setiap pembaca memiliki posisi bacanya sendiri dan melakukan perhitungan
availableToRead()
. - Dari sudut pandang pembaca yang lambat, antrean diperbolehkan untuk meluap;
hal ini dapat menyebabkan
availableToRead()
menampilkan nilai yang lebih besar dari ukuran antrean. Operasi baca pertama setelah {i>overflow<i} gagal dan mengakibatkan posisi baca untuk pembaca tersebut yang diatur sama dengan {i>write pointer<i} saat ini, apakah overflow dilaporkan melaluiavailableToRead()
.
Metode read()
dan write()
akan menampilkan
true
jika semua data yang diminta dapat (dan telah) ditransfer ke/dari
antrean. Metode ini tidak memblokir; mereka akan berhasil (dan kembali
true
), atau kegagalan pengembalian (false
) secara langsung.
Metode readBlocking()
dan writeBlocking()
menunggu
hingga operasi yang diminta dapat diselesaikan, atau sampai habis waktu (atau
Nilai timeOutNanos
0 berarti tidak pernah waktu tunggu habis).
Operasi pemblokiran diterapkan menggunakan kata penanda peristiwa. Secara {i>default<i},
setiap antrean membuat dan menggunakan kata penandanya sendiri untuk mendukung bentuk
readBlocking()
dan writeBlocking()
. Anda dapat menggunakan
beberapa antrean untuk berbagi satu kata, sehingga proses dapat menunggu operasi tulis atau
membaca ke antrean mana pun. Pointer ke kata penanda peristiwa dari antrean bisa
yang diperoleh dengan memanggil getEventFlagWord()
, dan pointer tersebut (atau
ke lokasi memori bersama yang sesuai) dapat digunakan untuk membuat
EventFlag
objek untuk diteruskan ke dalam bentuk
readBlocking()
, dan writeBlocking()
untuk akun
antrean. readNotification
dan writeNotification
parameter memberi tahu bit mana dalam penanda
kejadian yang harus digunakan untuk memberi sinyal pembacaan dan
yang menulis pada antrean tersebut. readNotification
dan
writeNotification
adalah bitmask 32-bit.
readBlocking()
menunggu pada writeNotification
bit;
jika parameternya 0, panggilan akan selalu gagal. Jika
Nilai readNotification
adalah 0, panggilan tidak gagal, tetapi
pembacaan yang berhasil tidak akan
menetapkan bit notifikasi apa pun. Dalam antrean yang tersinkronisasi,
ini berarti bahwa panggilan writeBlocking()
yang sesuai
tidak pernah aktif kecuali bit
diatur di tempat lain. Dalam antrean yang tidak tersinkronisasi,
writeBlocking()
tidak menunggu (masih harus digunakan untuk menyetel
menulis bit notifikasi), dan memang sesuai untuk pembacaan
bit notifikasi. Demikian pula, writeblocking()
akan gagal jika
readNotification
adalah 0, dan operasi tulis yang berhasil menetapkan
writeNotification
bit.
Untuk menunggu beberapa antrean sekaligus, gunakan objek EventFlag
wait()
untuk menunggu bitmask notifikasi. Tujuan
Metode wait()
menampilkan kata status dengan bit yang menyebabkan
bangun. Informasi ini kemudian digunakan untuk
memverifikasi antrean yang memiliki
cukup ruang atau data untuk operasi tulis/baca
yang diinginkan dan lakukan
write()
/read()
yang tidak memblokir. Untuk mendapatkan operasi pasca-operasi
gunakan panggilan lain keEventFlag
Metode wake()
. Untuk definisi EventFlag
abstraksi, mengacu pada
system/libfmq/include/fmq/EventFlag.h
.
Tidak ada operasi penyalinan
Tujuan
readBlocking
/read
/write
/writeBlocking()
API membawa pointer ke buffer input/output sebagai argumen dan menggunakan
memcpy()
memanggil secara internal untuk menyalin data antara
Buffering dering FMQ. Untuk meningkatkan kinerja, Android 8.0 dan yang lebih tinggi menyertakan serangkaian
API yang menyediakan akses pointer langsung ke buffer ring, sehingga menghilangkan
perlu menggunakan panggilan memcpy
.
Gunakan API publik berikut untuk operasi FMQ zero-copy:
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);
- Metode
beginWrite
memberikan pointer dasar ke dalam cincin FMQ {i>buffer <i}(penyangga). Setelah data ditulis, commit menggunakancommitWrite()
. MetodebeginRead
/commitRead
berfungsi dengan cara yang sama. - Metode
beginRead
/Write
menjadikan jumlah pesan yang akan dibaca/ditulis dan mengembalikan boolean yang menunjukkan apakah baca/tulis dimungkinkan. Jika operasi baca atau tulis memungkinkan,memTx
struct diisi dengan pointer dasar yang dapat digunakan untuk pointer langsung akses ke memori bersama buffer ring. - Struktur
MemRegion
berisi detail tentang blok memori, termasuk pointer dasar (alamat dasar blok memori) dan panjang dalam istilahT
(panjang blok memori dalam kaitannya dengan rentang jenis antrean pesan). - Struktur
MemTransaction
berisi duaMemRegion
struct,first
dansecond
sebagai perintah baca atau tulis buffer cincin mungkin memerlukan pembungkusan ke awal antrean. Ini akan berarti bahwa dua penunjuk dasar diperlukan untuk membaca/menulis data ke FMQ buffer ring.
Untuk mendapatkan alamat dasar dan panjang dari struct 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
Untuk mendapatkan referensi ke MemRegion
pertama dan kedua dalam
Objek MemTransaction
:
const MemRegion& getFirstRegion(); // get a reference to the first MemRegion const MemRegion& getSecondRegion(); // get a reference to the second MemRegion
Contoh penulisan ke FMQ menggunakan API zero copy:
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 }
Metode helper berikut juga merupakan bagian dari MemTransaction
:
T* getSlot(size_t idx);
Mengembalikan pointer ke slotidx
dalamMemRegions
yang merupakan bagian dariMemTransaction
ini . Jika objekMemTransaction
mewakili memori untuk membaca/menulis N item bertipe T, lalu rentang yang valid dariidx
adalah antara 0 dan N-1.bool copyTo(const T* data, size_t startIdx, size_t nMessages = 1);
TulisnMessages
item jenis T ke dalam region memori dijelaskan oleh objek, mulai dari indeksstartIdx
. Metode ini menggunakanmemcpy()
dan tidak dimaksudkan untuk digunakan pada salinan nol operasi. Jika objekMemTransaction
mewakili memori untuk membaca/menulis N item bertipe T, maka rentang yang valid dariidx
adalah antara 0 dan N-1.bool copyFrom(T* data, size_t startIdx, size_t nMessages = 1);
Metode helper untuk membacanMessages
item tipe T dari region memori yang dijelaskan oleh objek mulai daristartIdx
. Ini menggunakanmemcpy()
dan tidak dimaksudkan untuk digunakan untuk salinan nol operasi.
Kirim antrean melalui HIDL
Di sisi pembuatan:
- Buat objek antrean pesan seperti yang dijelaskan di atas.
- Pastikan bahwa objek valid dengan
isValid()
. - Jika Anda menunggu beberapa antrean dengan meneruskan
EventFlag
menjadi format panjangreadBlocking()
/writeBlocking()
, Anda dapat mengekstrak pointer flag peristiwa (menggunakangetEventFlagWord()
) dariMessageQueue
yang diinisialisasi untuk membuat flag, dan gunakan flag tersebut untuk membuat objekEventFlag
yang diperlukan. - Gunakan metode
MessageQueue
getDesc()
untuk mendapatkan objek deskriptor. - Dalam file
.hal
, berikan parameter jenis untuk metode tersebutfmq_sync
ataufmq_unsync
denganT
adalah yang sesuai dengan definisi HIDL. Gunakan metode ini untuk mengirim objek yang ditampilkan olehgetDesc()
ke proses penerimaan.
Di sisi penerima:
- Gunakan objek deskriptor untuk membuat objek
MessageQueue
. Menjadi pastikan untuk menggunakan ragam antrean dan tipe data yang sama, atau {i>template<i} gagal mengompilasi. - Jika Anda mengekstrak tanda peristiwa, ekstrak tanda dari penanda yang sesuai
MessageQueue
dalam proses penerimaan. - Gunakan objek
MessageQueue
untuk mentransfer data.