AIDL desteği arıyorsanız ayrıca bkz. AIDL ile FMQ .
HIDL'nin uzaktan prosedür çağrısı (RPC) altyapısı Binder mekanizmalarını kullanır; bu, çağrıların ek yük gerektirdiği, çekirdek işlemleri gerektirdiği ve zamanlayıcı eylemini tetikleyebileceği anlamına gelir. Ancak daha az ek yüke sahip ve çekirdek müdahalesi olmayan işlemler arasında veri aktarımının gerekli olduğu durumlarda Hızlı Mesaj Kuyruğu (FMQ) sistemi kullanılır.
FMQ istenilen özelliklere sahip mesaj kuyrukları oluşturur. Bir MQDescriptorSync
veya MQDescriptorUnsync
nesnesi, bir HIDL RPC çağrısı üzerinden gönderilebilir ve alıcı işlem tarafından mesaj kuyruğuna erişmek için kullanılabilir.
Hızlı Mesaj Kuyrukları yalnızca C++'da ve Android 8.0 ve üzerini çalıştıran cihazlarda desteklenir.
Mesaj Kuyruğu türleri
Android iki kuyruk türünü destekler ( tatlar olarak bilinir):
- Senkronize edilmemiş kuyrukların taşmasına izin verilir ve çok sayıda okuyucusu olabilir; her okuyucunun verileri zamanında okuması veya kaybetmesi gerekir.
- Senkronize kuyrukların taşmasına izin verilmez ve yalnızca bir okuyucu bulunabilir.
Her iki kuyruk türünün de taşmasına izin verilmez (boş bir kuyruktan okuma başarısız olur) ve yalnızca bir yazıcıya sahip olabilir.
senkronize edilmemiş
Senkronize olmayan bir kuyruğun yalnızca bir yazarı vardır ancak herhangi bir sayıda okuyucusu olabilir. Kuyruk için bir yazma konumu vardır; ancak her okuyucu kendi bağımsız okuma konumunu takip eder.
Yapılandırılan kuyruk kapasitesinden daha büyük olmadıkları sürece kuyruğa yazma işlemleri her zaman başarılı olur (taşma açısından kontrol edilmez) (kuyruk kapasitesinden daha büyük yazmalar hemen başarısız olur). Her okuyucunun farklı bir okuma konumu olabileceğinden, her okuyucunun her veri parçasını okumasını beklemek yerine, yeni yazma işlemlerinin alana ihtiyaç duyması durumunda verilerin kuyruktan düşmesine izin verilir.
Okuyucular verinin kuyruğun sonuna düşmeden önce alınmasından sorumludur. Mevcut olandan daha fazla veri okumaya çalışan bir okuma ya hemen başarısız olur (engellenmiyorsa) ya da yeterli verinin kullanılabilir olmasını bekler (engelliyorsa). Kuyruk kapasitesinden daha fazla veri okumaya çalışan bir okuma her zaman anında başarısız olur.
Bir okuyucu, yazara ayak uyduramazsa ve bu okuyucu tarafından yazılan ve henüz okunmayan veri miktarı kuyruk kapasitesinden daha büyükse, bir sonraki okuma veri döndürmez; bunun yerine okuyucunun okuma konumunu en son yazma konumuna eşit olacak şekilde sıfırlar ve ardından hata döndürür. Okunabilecek veriler taşma sonrasında ancak bir sonraki okumadan önce kontrol edilirse, sıra kapasitesinden daha fazla okunabilir veri gösterilir, bu da taşmanın meydana geldiğini gösterir. (Kuyruk, mevcut verilerin kontrol edilmesi ile bu verilerin okunmasının denenmesi arasında taşarsa, taşmanın tek göstergesi okumanın başarısız olmasıdır.)
Senkronize edilmemiş bir kuyruğun okuyucuları büyük olasılıkla kuyruğun okuma ve yazma işaretçilerini sıfırlamak istemez. Bu nedenle, tanımlayıcıdan kuyruk oluştururken okuyucular "resetPointers" parametresi için "false" bağımsız değişkenini kullanmalıdır.
senkronize
Senkronize bir kuyrukta, tek bir yazma konumu ve tek bir okuma konumu olan bir yazar ve bir okuyucu bulunur. Kuyruğun sahip olduğu alandan daha fazla veri yazmak veya kuyrukta mevcut olandan daha fazla veri okumak mümkün değildir. Engellemeli veya engellemesiz yazma veya okuma fonksiyonunun çağrılmasına bağlı olarak, kullanılabilir alanı veya veriyi aşma girişimleri ya hemen hataya neden olur ya da istenen işlem tamamlanana kadar bloke eder. Kuyruk kapasitesinden daha fazla veri okuma veya yazma girişimleri her zaman hemen başarısız olur.
FMQ'yu ayarlama
Bir ileti kuyruğu birden fazla MessageQueue
nesnesi gerektirir: biri yazılacak ve bir veya daha fazlası okunacak. Hangi nesnenin yazma veya okuma için kullanıldığına ilişkin açık bir yapılandırma yoktur; hem okuma hem de yazma için hiçbir nesnenin kullanılmamasını, en fazla bir yazarın olmasını ve senkronize kuyruklar için en fazla bir okuyucunun olmasını sağlamak kullanıcıya kalmıştır.
İlk Mesaj Sırası nesnesini oluşturma
Tek bir çağrıyla bir mesaj kuyruğu oluşturulur ve yapılandırılır:
#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)
başlatıcısı, mesaj kuyruğu işlevselliğini destekleyen bir nesne oluşturur ve başlatır. -
MessageQueue<T, flavor>(numElements, configureEventFlagWord)
başlatıcısı, engelleme ile mesaj kuyruğu işlevselliğini destekleyen bir nesne oluşturur ve başlatır. -
flavor
senkronize edilmiş bir kuyruk içinkSynchronizedReadWrite
veya senkronize edilmemiş bir kuyruk içinkUnsynchronizedWrite
olabilir. -
uint16_t
(bu örnekte), iç içe arabellekler (string
veyavec
türleri yok), tanıtıcılar veya arabirimler içermeyen herhangi bir HIDL tanımlı tür olabilir. -
kNumElementsInQueue
giriş sayısı cinsinden kuyruğun boyutunu gösterir; kuyruk için ayrılacak paylaşılan bellek arabelleğinin boyutunu belirler.
İkinciMessageQueue nesnesini oluşturma
Mesaj kuyruğunun ikinci tarafı, birinci taraftan elde edilen bir MQDescriptor
nesnesi kullanılarak oluşturulur. MQDescriptor
nesnesi, bir HIDL veya AIDL RPC çağrısı üzerinden mesaj kuyruğunun ikinci ucunu tutacak işleme gönderilir. MQDescriptor
, aşağıdakiler de dahil olmak üzere kuyrukla ilgili bilgileri içerir:
- Tamponu eşlemek ve işaretçiyi yazmak için bilgi.
- Okuma işaretçisini eşlemeye yönelik bilgiler (kuyruk senkronize edilmişse).
- Olay bayrağı sözcüğünü eşlemeye yönelik bilgiler (kuyruk engelleniyorsa).
- HIDL tanımlı kuyruk öğeleri türünü ve kuyruk lezzetini (senkronize edilmiş veya senkronize edilmemiş) içeren nesne türü (
<T, flavor>
).
MQDescriptor
nesnesi bir MessageQueue
nesnesi oluşturmak için kullanılabilir:
MessageQueue<T, flavor>::MessageQueue(const MQDescriptor<T, flavor>& Desc, bool resetPointers)
resetPointers
parametresi, bu MessageQueue
nesnesini oluştururken okuma ve yazma konumlarının 0'a sıfırlanıp sıfırlanmayacağını gösterir. Senkronize edilmemiş bir kuyrukta, okuma konumu (senkronize edilmemiş kuyruklardaki her MessageQueue
nesnesi için yereldir) oluşturma sırasında her zaman 0'a ayarlanır. Tipik olarak MQDescriptor
, ilk mesaj kuyruğu nesnesinin oluşturulması sırasında başlatılır. Paylaşılan bellek üzerinde ekstra kontrol sağlamak için, MQDescriptor
manuel olarak ayarlayabilir ( MQDescriptor
system/libhidl/base/include/hidl/MQDescriptor.h
dosyasında tanımlanmıştır) ve ardından bu bölümde açıklandığı gibi her bir MessageQueue
nesnesini oluşturabilirsiniz.
Kuyrukları ve etkinlik bayraklarını engelleme
Varsayılan olarak kuyruklar okuma/yazma işlemlerinin engellenmesini desteklemez. Okuma/yazma çağrılarını engellemenin iki türü vardır:
- Kısa form , üç parametreli (veri işaretçisi, öğe sayısı, zaman aşımı). Tek bir kuyrukta bireysel okuma/yazma işlemlerinin engellenmesini destekler. Bu formu kullanırken kuyruk, olay bayrağını ve bit maskelerini dahili olarak işleyecektir ve ilk mesaj kuyruğu nesnesinin ikinci bir
true
parametresiyle başlatılması gerekir. Örneğin:// For an unsynchronized FMQ that supports blocking mFmqUnsynchronizedBlocking = new (std::nothrow) MessageQueue<uint16_t, kUnsynchronizedWrite> (kNumElementsInQueue, true /* enable blocking operations */);
- Altı parametreli uzun biçim (olay bayrağını ve bit maskelerini içerir). Birden çok kuyruk arasında paylaşılan bir
EventFlag
nesnesinin kullanılmasını destekler ve kullanılacak bildirim bit maskelerinin belirlenmesine olanak tanır. Bu durumda, her okuma ve yazma çağrısına olay bayrağı ve bit maskelerinin sağlanması gerekir.
Uzun biçim için, EventFlag
her readBlocking()
ve writeBlocking()
çağrısında açıkça sağlanabilir. Kuyruklardan biri dahili bir olay bayrağıyla başlatılabilir; bunun daha sonra getEventFlagWord()
kullanılarak kuyruğun MessageQueue
nesnelerinden çıkarılması ve diğer FMQ'larla kullanılmak üzere her işlemde EventFlag
nesneleri oluşturmak için kullanılması gerekir. Alternatif olarak EventFlag
nesneleri herhangi bir uygun paylaşımlı bellekle başlatılabilir.
Genel olarak her sıra, engellemesiz, kısa biçimli engelleme veya uzun biçimli engellemeden yalnızca birini kullanmalıdır. Bunları karıştırmak bir hata değildir ancak istenilen sonucu elde etmek için dikkatli bir programlama gerekir.
Belleği salt okunur olarak işaretleme
Varsayılan olarak, paylaşılan belleğin okuma ve yazma izinleri vardır. Senkronize edilmemiş kuyruklar için ( kUnsynchronizedWrite
), yazar, MQDescriptorUnsync
nesnelerini dağıtmadan önce tüm okuyucuların yazma izinlerini kaldırmak isteyebilir. Bu, okuyucu süreçlerindeki hatalara veya kötü davranışlara karşı koruma sağlamak için önerilen diğer süreçlerin kuyruğa yazamamasını sağlar. Yazar, okuyucuların kuyruğun okuma tarafını oluşturmak için MQDescriptorUnsync
kullandıklarında kuyruğu sıfırlayabilmelerini istiyorsa, bellek salt okunur olarak işaretlenemez. Bu, 'MessageQueue' yapıcısının varsayılan davranışıdır. Dolayısıyla, bu kuyruğun zaten mevcut kullanıcıları varsa, kuyruğu resetPointer=false
ile oluşturmak için kodlarının değiştirilmesi gerekir.
- Yazar:
ashmem_set_prot_region
MQDescriptor
dosya tanımlayıcısı ve salt okunur (PROT_READ
) olarak ayarlanmış bölgeyle çağırın:int res = ashmem_set_prot_region(mqDesc->handle->data[0], PROT_READ)
- Okuyucu:
resetPointer=false
ile mesaj kuyruğu oluşturun (varsayılantrue
):mFmq = new (std::nothrow) MessageQueue(mqDesc, false);
Mesaj Sırasını Kullanma
MessageQueue
nesnesinin genel API'si şöyledir:
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()
ve availableToRead()
tek bir işlemde ne kadar verinin aktarılabileceğini belirlemek için kullanılabilir. Senkronize edilmemiş bir kuyrukta:
-
availableToWrite()
her zaman kuyruğun kapasitesini döndürür. - Her okuyucunun kendi okuma konumu vardır ve
availableToRead()
için kendi hesaplamasını yapar. - Yavaş bir okuyucunun bakış açısından kuyruğun taşmasına izin verilir; bu,
availableToRead()
işlevinin kuyruk boyutundan daha büyük bir değer döndürmesine neden olabilir. Taşma sonrasındaki ilk okuma başarısız olur ve taşmaavailableToRead()
aracılığıyla rapor edilmiş olsun ya da olmasın, söz konusu okuyucunun okuma konumunun geçerli yazma işaretçisine eşit olarak ayarlanmasına neden olur.
İstenen tüm veriler kuyruğa aktarılabiliyorsa (ve aktarılmışsa) read()
ve write()
yöntemleri true
döndürür. Bu yöntemler engellemez; ya başarılı olurlar (ve true
değerini döndürürler) ya da hemen başarısızlık ( false
) değerini döndürürler.
readBlocking()
ve writeBlocking()
yöntemleri, istenen işlem tamamlanana veya zaman aşımına uğrayana kadar bekler ( timeOutNanos
değerinin 0 olması, hiçbir zaman zaman aşımı olmadığı anlamına gelir).
Engelleme işlemleri bir olay bayrağı sözcüğü kullanılarak uygulanır. Varsayılan olarak her kuyruk, readBlocking()
ve writeBlocking()
kısa biçimini desteklemek için kendi bayrak sözcüğünü oluşturur ve kullanır. Birden fazla kuyruğun tek bir kelimeyi paylaşması mümkündür, böylece bir işlem kuyruklardan herhangi birine yazma veya okuma işlemlerini bekleyebilir. Bir kuyruğun olay bayrağı sözcüğüne yönelik bir işaretçi, getEventFlagWord()
çağrılarak elde edilebilir ve bu işaretçi (veya uygun bir paylaşılan bellek konumuna yönelik herhangi bir işaretçi), readBlocking()
in uzun biçimine geçmek üzere bir EventFlag
nesnesi oluşturmak için kullanılabilir ve Farklı bir kuyruk için writeBlocking()
. readNotification
ve writeNotification
parametreleri, olay bayrağındaki hangi bitlerin o kuyruktaki okuma ve yazma sinyallerini vermek için kullanılması gerektiğini söyler. readNotification
ve writeNotification
32 bitlik bit maskeleridir.
readBlocking()
writeNotification
bitlerini bekler; bu parametre 0 ise çağrı her zaman başarısız olur. readNotification
değeri 0 ise çağrı başarısız olmaz ancak başarılı bir okuma herhangi bir bildirim bitini ayarlamaz. Senkronize bir kuyrukta bu, karşılık gelen writeBlocking()
çağrısının, bit başka bir yere ayarlanmadığı sürece asla uyanmayacağı anlamına gelir. Senkronize olmayan bir kuyrukta writeBlocking()
beklemez (yine de yazma bildirim bitini ayarlamak için kullanılmalıdır) ve okumaların herhangi bir bildirim biti ayarlamaması uygundur. Benzer şekilde, readNotification
0 ise writeblocking()
başarısız olur ve başarılı bir yazma, belirtilen writeNotification
bitlerini ayarlar.
Aynı anda birden fazla kuyrukta beklemek için, bildirimlerin bit maskesini beklemek amacıyla EventFlag
nesnesinin wait()
yöntemini kullanın. wait()
yöntemi, uyandırma ayarına neden olan bitleri içeren bir durum sözcüğünü döndürür. Bu bilgi daha sonra ilgili kuyruğun istenen yazma/okuma işlemi için yeterli alana veya veriye sahip olduğunu doğrulamak ve engellemeyen bir write()
/ read()
gerçekleştirmek için kullanılır. İşlem sonrası bildirimi almak için EventFlag
wake()
yöntemine yapılan başka bir çağrıyı kullanın. EventFlag
soyutlamasının tanımı için system/libfmq/include/fmq/EventFlag.h
bakın.
Sıfır kopyalama işlemi
read
/ write
/ readBlocking
/ writeBlocking()
API'leri argüman olarak bir giriş/çıkış arabelleğine yönelik bir işaretçi alır ve verileri aynı ile FMQ halka arabelleği arasında kopyalamak için dahili olarak memcpy()
çağrılarını kullanır. Performansı artırmak için Android 8.0 ve üzeri, halka arabelleğine doğrudan işaretçi erişimi sağlayan ve memcpy
çağrılarını kullanma ihtiyacını ortadan kaldıran bir dizi API içerir.
Sıfır kopya FMQ işlemleri için aşağıdaki genel API'leri kullanın:
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
yöntemi, FMQ halka arabelleğine temel işaretçiler sağlar. Veriler yazıldıktan sonracommitWrite()
kullanarak kaydedin.beginRead
/commitRead
yöntemleri aynı şekilde hareket eder. -
beginRead
/Write
yöntemleri, okunacak/yazılacak mesaj sayısını girdi olarak alır ve okuma/yazmanın mümkün olup olmadığını belirten bir boole değeri döndürür. Okuma veya yazma mümkünsememTx
yapısı, halka arabellek paylaşımlı belleğine doğrudan işaretçi erişimi için kullanılabilen temel işaretçilerle doldurulur. -
MemRegion
yapısı, temel işaretçi (bellek bloğunun temel adresi) veT
cinsinden uzunluk (mesaj kuyruğunun HIDL tanımlı türü açısından bellek bloğunun uzunluğu) dahil olmak üzere bir bellek bloğu hakkında ayrıntılar içerir. -
MemTransaction
yapısı ikiMemRegion
yapısı içerir;first
vesecond
olarak halka arabelleğine okuma veya yazma işlemi kuyruğun başlangıcına kadar sarma gerektirebilir. Bu, FMQ halka arabelleğine veri okumak/yazmak için iki temel işaretçinin gerekli olduğu anlamına gelir.
Bir MemRegion
yapısından temel adresi ve uzunluğu almak için:
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
Bir MemTransaction
nesnesi içindeki birinci ve ikinci MemRegion
referanslar almak için:
const MemRegion& getFirstRegion(); // get a reference to the first MemRegion const MemRegion& getSecondRegion(); // get a reference to the second MemRegion
Sıfır kopya API'lerini kullanarak FMQ'ya yazma örneği:
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 }
Aşağıdaki yardımcı yöntemler de MemTransaction
bir parçasıdır:
-
T* getSlot(size_t idx);
BuMemTransaction
nesnesinin parçası olanMemRegions
içindeki yuvaidx
bir işaretçi döndürür.MemTransaction
nesnesi T tipi N öğeyi okumak/yazmak için bellek bölgelerini temsil ediyorsa, geçerliidx
aralığı 0 ile N-1 arasındadır. -
bool copyTo(const T* data, size_t startIdx, size_t nMessages = 1);
T türündekinMessages
öğelerinistartIdx
dizininden başlayarak nesne tarafından tanımlanan bellek bölgelerine yazın. Bu yöntemmemcpy()
işlevini kullanır ve sıfır kopyalama işlemi için kullanılması amaçlanmamıştır.MemTransaction
nesnesi T tipi N öğeyi okumak/yazmak için belleği temsil ediyorsa, geçerliidx
aralığı 0 ile N-1 arasındadır. -
bool copyFrom(T* data, size_t startIdx, size_t nMessages = 1);
startIdx
başlayan nesne tarafından tanımlanan bellek bölgelerinden T türündekinMessages
öğelerini okumaya yönelik yardımcı yöntem. Bu yöntemmemcpy()
işlevini kullanır ve sıfır kopyalama işlemi için kullanılması amaçlanmamıştır.
Sıranın HIDL üzerinden gönderilmesi
Oluşturma tarafında:
- Yukarıda açıklandığı gibi mesaj kuyruğu nesnesini oluşturun.
- Nesnenin
isValid()
ile geçerli olduğunu doğrulayın. - Bir
EventFlag
uzunreadBlocking()
/writeBlocking()
biçimine geçirerek birden fazla kuyrukta bekleyecekseniz, olay bayrağı işaretçisini (getEventFlagWord()
kullanarak) bayrağı oluşturmak için başlatılan birMessageQueue
nesnesinden çıkarabilirsiniz. ve gerekliEventFlag
nesnesini oluşturmak için bu bayrağı kullanın. - Bir tanımlayıcı nesne almak için
MessageQueue
getDesc()
yöntemini kullanın. -
.hal
dosyasında yöntemefmq_sync
türünde bir parametre verinveya fmq_unsync
burada T
uygun bir HIDL tanımlı türdür.getDesc()
tarafından döndürülen nesneyi alma işlemine göndermek için bunu kullanın.
Alıcı tarafta:
- Bir
MessageQueue
nesnesi oluşturmak için tanımlayıcı nesneyi kullanın. Aynı kuyruk türünü ve veri türünü kullandığınızdan emin olun, aksi takdirde şablon derlenemez. - Bir olay bayrağını ayıkladıysanız, alma işleminde ilgili
MessageQueue
nesnesinden bayrağı çıkarın. - Verileri aktarmak
MessageQueue
nesnesini kullanın.