Hızlı Mesaj Sırası (FMQ)

AIDL desteği için aşağıdaki kaynaklara da bakabilirsiniz: AIDL ile FMQ.

HIDL'nin uzak prosedür çağrısı (RPC) altyapısı Bağlayıcı mekanizmaları kullanır, Yani çağrılar ek yük gerektirir, çekirdek işlemleri gerektirir ve planlayıcı işlemidir. Ancak, özellikle mobil ağlar arasında veri aktarımının gerekli olduğu daha az ek yük ve çekirdek dahil olmayan süreçlerle, Fast Message Queue (FMQ) sisteminin kullanılması.

FMQ, istenen özelliklerle mesaj sıraları oluşturur. MQDescriptorSync veya MQDescriptorUnsync nesnesi olabilir HIDL RPC çağrısı üzerinden gönderilmiş ve alma işlemi tarafından ileti sırası.

Hızlı Mesaj Sıraları yalnızca C++ ve cihazlarda desteklenir Android 8.0 ve daha yeni bir sürüm kullanıyorsanız.

MessageQueue türleri

Android, iki sıra türünü (aroma olarak bilinir) destekler:

  • Senkronize edilmemiş sıraların taşmasına izin verilir ve çok sayıda okuyucular; Her okuyucunun verileri zamanında okuması gerekir; aksi halde bu verileri kaybedebilir.
  • Senkronize edilmiş sıraların taşmasına izin verilmez ve yalnızca bir okuyucu.

Her iki sıra türünün de göz ardı edilmesine izin verilmez (boş bir kuyruktan okuma) başarısız olur) ve yalnızca bir yazarı olabilir.

Senkronize edilmedi

Senkronize edilmemiş bir sıranın yalnızca bir yazarı vardır, ancak okuyucular. Sıra için bir yazma konumu vardır. ancak her okuyucu okuma konumunu izlemesine olanak tanır.

Sıraya yazma işlemleri yapılandırılmış sıra kapasitesinden ( sıra kapasitesi hemen başarısız olur. Her okuyucunun okuması farklı olabilir her okuyucunun her veri parçasını okumasını beklemek yerine, yeni yazmalar için alana ihtiyaç duyulduğunda sıradan çıkmasına izin verilir.

Okuyucular, sıraya ekleyebilirsiniz. Mevcut olandan daha fazla veri okumaya çalışan bir okuma hemen başarısız olursa (engelleme yoksa) veya yeterli verinin kullanılabilmesini beklerken (eğer engelleme). Her zaman sıra kapasitesinden daha fazla veri okumaya çalışan okuma işlemi hemen başarısız olur.

Okuyucu, bir yazarın gerisinde kalmazsa okuyucu tarafından henüz okunmamış olması ihtimaline karşı bir sonraki okuma veri döndürmez; okuyucunun okuma hızını sıfırlar konumunu en son yazma konumuna eşit olacak şekilde ayarlayıp hata döndürür. Öğe okunabilecek veriler taşma işleminden sonra kontrol edilir, ancak bir sonraki okuma işleminden önce sıra kapasitesinden daha fazla verinin okunabileceğini gösterir. taşma oluştu. (Sıra, kullanılabilir veriler kontrol edildikten sonra taşarsa çalışırken, taşmanın tek göstergesi okuma işlemi başarısız olur.)

Senkronize edilmemiş bir sıranın okuyucuları, büyük olasılıkla sıfırlamak istemiyor sıranın okuma ve yazma işaretçilerini kullanır. Bu nedenle, tanımlayıcı okuyucular "resetPointers" için "false" bağımsız değişkeni kullanmalıdır parametresinden sonra bir değer girin.

Senkronize edildi

Senkronize edilmiş bir sıranın tek bir yazma özelliğine sahip bir yazarı ve bir okuyucusu vardır ve tek bir okuma konumuna sahip olur. Bir ekipten çok daha fazla sırada, sıranın şu anda bulundurduğundan daha fazla veri için alan vardır veya bu verilerin okunduğundan daha fazladır. Engellemeyen veya engellemeyen yazma ya da okuma işlevinin çağrılır, kullanılabilir alanı aşmaya çalışır veya veri döndürme hatası hemen kullanabilir veya istenen işlem tamamlanana kadar engelleyebilirsiniz. Şunları dener: sıra kapasitesinden daha fazla veri okuma veya yazma işlemi her zaman anında başarısız olur.

FMQ kurulumu

İleti sırası için birden fazla MessageQueue nesne gerekiyor: biri - bir veya daha çok kaynak okunabilir. Uygunsuz bir belge yok yazma veya okuma için kullanılacak nesnenin yapılandırılması; konu hakkında emin olmak için kullanıcının hem okuma hem de yazma için en fazla bir yazar olabilir ve senkronize edilmiş sıralar için yardımcı olur.

İlk MessageQueue nesnesini oluşturma

Bir mesaj sırası tek bir çağrıyla 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ı İleti sırası işlevini destekleyen bir nesne oluşturur ve başlatır.
  • MessageQueue<T, flavor>(numElements, configureEventFlagWord) başlatıcısı bir nesne oluşturur ve ilk kullanıma hazırlar engelleme özelliğiyle ileti sırası işlevini destekleyen
  • flavor, değeri için kSynchronizedReadWrite olabilir senkronize edilmiş sıra veya senkronize edilmemiş bir öğe için kUnsynchronizedWrite sıra.
  • uint16_t (bu örnekte) herhangi bir olabilir HIDL tarafından tanımlanmış tür: iç içe yerleştirilmiş arabellekler (string veya vec değil) içermez kullanıcı adları veya arayüzler.
  • kNumElementsInQueue, sıranın şu sayıda boyutunu gösterir: girişlerin sayısı; bu işlem, ayrılan depolama alanına ayrılan paylaşılan bellek arabelleğinin boyutunu sıraya ekleyin.

İkinci MessageQueue nesnesini oluşturma

İleti sırasının ikinci tarafı İlk taraftan MQDescriptor nesne alındı. İlgili içeriği oluşturmak için kullanılan İşleme bir HIDL veya AIDL RPC çağrısı üzerinden MQDescriptor nesnesi gönderilir bulunur. İlgili içeriği oluşturmak için kullanılan MQDescriptor, sırayla ilgili aşağıdakiler gibi bilgileri içerir:

  • Arabelleği eşleme ve işaretçi yazma bilgileri.
  • Okuma işaretçisini eşleyecek bilgiler (sıra senkronize edilmişse).
  • Etkinlik işareti kelimeyi eşleyecek bilgiler (sıradaki engelleme engelleniyorsa).
  • Şunları içeren nesne türü (<T, flavor>): HIDL tarafından tanımlanan tür: sıra öğeleri ve sıra aroma (senkronize edilmiş veya senkronize edilmemiş).

MQDescriptor nesnesi, MessageQueue nesne:

MessageQueue<T, flavor>::MessageQueue(const MQDescriptor<T, flavor>& Desc, bool resetPointers)

resetPointers parametresi, okuma değerinin sıfırlanıp sıfırlanmayacağını gösterir ve bu MessageQueue nesnesini oluştururken konumları 0 olarak yazın. Senkronize edilmemiş bir sırada, okuma konumu (her bir senkronize edilmemiş sıralardaki MessageQueue nesnesi) her zaman 0 değerine ayarlanır dikkat edin. MQDescriptor, genellikle şu işlem sırasında başlatılır: ilk ileti sırası nesnesinin oluşturulması. Paylaşılan ortak kontroller üzerinde ekstra kontrol kullanıyorsanız MQDescriptor cihazını manuel olarak ayarlayabilirsiniz (MQDescriptor, şurada tanımlanıyor: system/libhidl/base/include/hidl/MQDescriptor.h) daha sonra, bu bölümde açıklandığı şekilde her MessageQueue nesnesini oluşturun.

Sıraları ve etkinlik işaretlerini engelle

Varsayılan olarak, sıralar okuma/yazma işlemlerini engellemeyi desteklemez. İki türü vardır engellemesiyle ilgili daha fazla bilgi edinin:

  • Üç parametre (veri işaretçisi, öğe sayısı, zaman aşımı) ekleyebilirsiniz. Tek bir cihazda bağımsız okuma/yazma işlemlerinde engellemeyi destekler sıra. Bu form kullanılırken sıra, etkinlik işaretini ve bit maskelerini işler ve ilk ileti sırası nesnesi ikinci bir true parametresiyle başlatılmalıdır. Örnek:
    // For an unsynchronized FMQ that supports blocking
    mFmqUnsynchronizedBlocking =
      new (std::nothrow) MessageQueue<uint16_t, kUnsynchronizedWrite>
          (kNumElementsInQueue, true /* enable blocking operations */);
    
    .
  • Altı parametreli (etkinlik bayrağı ve bit maskeleri dahil) uzun biçim. Birden fazla sıra arasında paylaşılan EventFlag nesnesinin kullanılmasını destekler ve kullanılacak bildirim bit maskelerinin belirtilmesine olanak tanır. Bu durumda, her okuma ve yazma çağrısına etkinlik işareti ve bit maskeleri sağlanmalıdır.

Uzun biçim için EventFlag açıkça Her bir readBlocking() ve writeBlocking() çağrısı. Şunlardan biri: sıralar, dahili bir etkinlik işaretiyle başlatılabilir. Bunun ardından kullanılarak sıranın MessageQueue nesnesinden ayıklandı. getEventFlagWord() ve EventFlag oluşturmak için kullanıldı her işlemde başka FMQ'larla kullanmak için. Alternatif olarak, EventFlag nesne, uygun paylaşılan herhangi bir nesneyle başlatılabilir hafızada bulabilirsiniz.

Genel olarak her sıra, engellemeyen, kısa biçimli videolardan yalnızca birini kullanmalıdır. veya uzun biçimli engellemedir. Bunları karıştırmak bir hata değildir ancak programlamanın önemi var.

Hafızayı salt okunur olarak işaretle

Paylaşılan bellek, varsayılan olarak okuma ve yazma izinlerine sahiptir. Senkronize edilmemiş sıralar (kUnsynchronizedWrite) varsa yazar tüm cihazlar için yazma izinlerini kaldırmak MQDescriptorUnsync nesneleri bırakmadan önce okurlar. Bu, diğer işlemleri sıraya yazamaz. Bu, hatalara ve kötü davranışlara karşı koruma için önerilir. kısa bir ifadedir. Yazar, Sıranın okunan tarafını oluşturmak için MQDescriptorUnsync işlemi gerçekleştirildiğinde anı işaretlenemez. salt okunur olarak belirleyin. Bu, "MessageQueue" kurucusunun varsayılan davranışıdır. Önceden oluşturulmuş varsa, bu kullanıcıların kodlarını değiştirerek sıra oluşturmak için resetPointer=false

  • Yazar: MQDescriptor dosya tanımlayıcısıyla ashmem_set_prot_region öğesini çağırın ve bölge salt okunur (PROT_READ) olarak ayarlanmış:
    int res = ashmem_set_prot_region(mqDesc->handle->data[0], PROT_READ)
  • Reader: resetPointer=false ile ileti sırası oluşturun ( varsayılan değer true):
    mFmq = new (std::nothrow) MessageQueue(mqDesc, false);

MessageQueue'yu kullanma

MessageQueue nesnesinin herkese açık API'si:

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() kullanılabilir tek bir işlemde ne kadar veri aktarılabileceğini belirleyin. senkronize edilmemiş sıra:

  • availableToWrite(), her zaman sıranın kapasitesini döndürür.
  • Her okuyucunun kendi okuma konumu vardır ve okuyucunun availableToRead()
  • Yavaş bir okuyucu açısından sıranın taşmasına izin verilir. bu, availableToRead() ürününün şundan daha büyük bir değer döndürmesine neden olabilir: sıranın boyutunu belirler. Bir taşma başarısız olduktan sonraki ilk okuma işlemi o okuyucunun okuma konumu, geçerli yazma işaretçisine eşit olarak ayarlanır. taşmanın Google Ads üzerinden availableToRead()

read() ve write() yöntemleri şu sonucu döndürür: İstenen tüm veriler şuralara/kaynaklardan aktarılabiliyorsa (ve bu veriler aktarılmışsa) true sıraya ekleyebilirsiniz. Bu yöntemler ya başarılı olur (ve ya da true) veya hemen iade hatası (false) olabilir.

readBlocking() ve writeBlocking() yöntemleri bekler istenen işlem tamamlanana kadar veya işlem zaman aşımına uğrayana kadar (bir timeOutNanos değerinin 0 olması hiçbir zaman zaman aşımına uğramayacağı anlamına gelir).

Engelleme işlemleri, bir etkinlik işareti kelimesi kullanılarak uygulanır. Varsayılan olarak her bir sıra, hikayenin kısa biçimini desteklemek için kendi işaret kelimesini readBlocking() ve writeBlocking(). Birbirinden farklı tek bir kelimeyi paylaşmak için birden fazla kuyruğa sahiptir. Böylece işlem, yazma veya okumayı kuyruğa sokar. Bir sıranın etkinlik işareti kelimesine işaret eden getEventFlagWord() çağrısıyla elde edilir ve bu işaretçi (veya uygun bir paylaşılan bellek konumuna işaret etme) kullanılabilir. EventFlag nesne Farklı bir kullanıcı için readBlocking() ve writeBlocking() sıra. readNotification ve writeNotification Parametreler, okuma ve veri işleme göstergeleri için etkinlik işaretindeki hangi bitlerin kullanılması gerektiğini belirtir yazması için bir fırsattır. readNotification ve writeNotification, 32 bit bit maskeleridir.

readBlocking(), writeNotification bitte bekler; bu parametre 0 ise çağrı her zaman başarısız olur. Öğe readNotification değeri 0 ise çağrı başarısız olmaz ancak başarılı okuma işlemi herhangi bir bildirim biti ayarlamaz. Senkronize edilmiş bir sırada, bu, ilgili writeBlocking() çağrısının Bit başka bir yere ayarlanmadığı sürece hiçbir zaman uyanmaz. Senkronize edilmemiş bir sırada, writeBlocking() beklemez (yine de yazma bildirimi biti) kullanır ve okumaların, herhangi bir değer bildirim bit'leri için de geçerlidir. Benzer şekilde, writeblocking() readNotification değeri 0'dır ve başarılı bir yazma işlemi, belirtilen writeNotification bit.

Aynı anda birden fazla sırayı beklemek için EventFlag nesnesinin Bildirim bit maskesi üzerinde beklemek için wait() yöntemi. İlgili içeriği oluşturmak için kullanılan wait() yöntemi, uyanma vakti geldi. Bu bilgiler daha sonra ilgili sıranın mevcut olmadığını doğrulamak için kullanılır. istenen yazma/okuma işlemi için yeterli alan veya veri sağlayın ve engellenmeyen write()/read(). Yükleme işlemi almak için bildirim için başka bir çağrı kullanın: EventFlag wake() yöntemi. EventFlag tanımı için soyutlama, referans system/libfmq/include/fmq/EventFlag.h

Sıfır kopyalama işlemi

İlgili içeriği oluşturmak için kullanılan read/write/readBlocking/writeBlocking() API'ler, işaretçiyi bağımsız değişken olarak giriş/çıkış arabelleğine götürür ve memcpy() aynı ile FMQ halka arabelleği. Performansı iyileştirmek için Android 8.0 ve sonraki sürümler, Halka arabelleğine doğrudan işaretçi erişimi sağlayarak memcpy çağrılarını kullanmanız gerekiyor.

Sıfır kopyalı FMQ işlemleri için aşağıdaki herkese açık 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 halkasına taban işaretçiler sağlar arabellek. Veriler yazıldıktan sonra commitWrite() işlevini kullanarak kaydedin. beginRead/commitRead yöntemleri aynı şekilde çalışır.
  • beginRead/Write yöntemleri okunacak/yazılacak mesajların sayısını belirler ve olanak tanır. Okuma veya yazma mümkünse memTx struct, doğrudan işaretçi için kullanılabilecek temel işaretçilerle doldurulur halka arabelleği paylaşılan belleğe erişme.
  • MemRegion struct, bir bellek bloğuyla ilgili ayrıntıları içerir. taban işaretçi (hafıza bloğunun ana adresi) ve uzunluğu da dahil olmak üzere T (HIDL tarafından tanımlanan sıranın türünü seçin).
  • MemTransaction yapısı iki MemRegion içerir struct, first ve second okuma veya yazma olarak kullanılır halka arabelleğinde, sıranın başına sarmalama gerekebilir. Bu FMQ'ya veri okumak/yazmak için iki temel işaretçinin olması gerektiği anlamına gelir. halka arabelleği.

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

Belirli bir zaman dilimi içinde birinci ve ikinci MemRegion ile ilgili referanslar almak için MemTransaction nesne:

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 kapsamındadır:

  • T* getSlot(size_t idx);.
    Şunun içindeki idx alanına bir işaretçi döndürür: Bu MemTransaction kapsamındaki MemRegions nesnesini tanımlayın. MemTransaction nesnesi, belleği temsil ediyorsa T türünde N öğe okunacak/yazılacak bölge, ardından geçerli aralık idx, 0 ile N-1 arasındadır.
  • bool copyTo(const T* data, size_t startIdx, size_t nMessages = 1);.
    Bellek bölgelerine T türünde nMessages öğe yazın startIdx dizininden başlayarak nesne tarafından açıklanır. Bu yöntem memcpy() kullanıyor ve sıfır kopya için kullanılmamalıdır işlemidir. MemTransaction nesnesi, T türünde N öğe okuma/yazma yapıyorsanız geçerli idx aralığı arasında yer alır.
  • bool copyFrom(T* data, size_t startIdx, size_t nMessages = 1);.
    nMessages startIdx tarihinden itibaren nesne tarafından açıklanan bellek bölgeleri. Bu yönteminde memcpy() kullanılmaktadır ve sıfır kopya için kullanılmamalıdır işlemidir.

Sırayı HIDL üzerinden gönder

İçerik üretme tarafında:

  1. Yukarıda açıklandığı şekilde ileti sırası nesnesi oluşturun.
  2. isValid() ile nesnenin geçerli olduğunu doğrulayın.
  3. Birden fazla sıra bekliyorsanız EventFlag biçiminin uzun biçimine readBlocking()/writeBlocking() etkinlik bayrağı işaretçisi (getEventFlagWord() kullanılarak) İşareti oluşturmak için başlatılan MessageQueue nesnesi ve gerekli EventFlag nesnesini oluşturmak için bu işareti kullanın.
  4. MessageQueue getDesc() yöntemini kullanarak açıklayıcı nesneden en iyi şekilde yararlanabilirsiniz.
  5. .hal dosyasında yönteme şu türde bir parametre verin: fmq_sync veya fmq_unsync, burada T bir ve HIDL tarafından tanımlanan uygun bir türdür. Bu komutu, getDesc() tarihinde almalısınız.

Alıcı tarafta:

  1. MessageQueue nesnesi oluşturmak için açıklayıcı nesneyi kullanın. aynı sıra aromasını ve veri türünü kullandığınızdan emin olun. Aksi takdirde, şablon derler.
  2. Bir etkinlik işaretini çıkardıysanız Alıcı işlemde MessageQueue nesne var.
  3. Verileri aktarmak için MessageQueue nesnesini kullanın.