Čo je prstencový buffer?

Kruhová vyrovnávacia pamäť je tiež známa ako fronta alebo cyklická vyrovnávacia pamäť a je bežnou formou frontu. Toto je populárny, ľahko implementovateľný štandard, a hoci je reprezentovaný ako kruh, v základnom kóde je lineárny. Kruhový rad existuje ako pole s pevnou dĺžkou s dvoma ukazovateľmi: jeden predstavuje začiatok frontu a druhý predstavuje chvost. Nevýhodou metódy je jej pevná veľkosť. Pre fronty, kde je potrebné pridávať a odstraňovať položky v strede, nielen na začiatku a na konci vyrovnávacej pamäte, je preferovaným prístupom implementácia ako prepojený zoznam.

Teoretické základy nárazníka

Teoretické základy nárazníka

Po pochopení základnej teórie je pre používateľa jednoduchšie zvoliť si efektívnu štruktúru poľa. Cyklická vyrovnávacia pamäť je dátová štruktúra, kde je pole spracované a vizualizované ako cykly, to znamená, že indexy sa po dosiahnutí dĺžky poľa vrátia na 0. Toto sa vykonáva pomocou dvoch ukazovateľov na pole: "hlava" a "chvost". Po pridaní údajov do vyrovnávacej pamäte sa ukazovateľ hlavičky posunie nahor. Podobne, keď sú odstránené, chvost sa tiež pohybuje hore. Definícia hlavy, chvosta, smer ich pohybu, miesto písania a čítania závisí od implementácie schémy.

Kruhové nárazníky sa príliš efektívne používajú na vyriešiť problémy spotrebiteľov. To znamená, že jedno vykonávacie vlákno je zodpovedné za produkciu údajov a druhé za spotrebu. Vo vstavaných zariadeniach s veľmi nízkou a strednou úrovňou je výrobca zastúpený vo formáte ISR (informácie získané zo senzorov) a spotrebiteľ je zastúpený vo forme cyklu hlavných udalostí.

Funkciou cyklických vyrovnávacích pamätí je, že sú implementované bez potreby zámkov v prostredí jedného výrobcu a jedného spotrebiteľa. To z nich robí ideálnu informačnú štruktúru pre vstavané programy. Ďalším rozdielom je, že neexistuje presný spôsob, ako odlíšiť vyplnený sektor od prázdneho. Je to tak preto, lebo v oboch prípadoch sa hlava spája s chvostom. Existuje mnoho spôsobov a riešení, ako sa s tým vysporiadať, ale väčšina z nich prináša veľa zmätkov a sťažuje čítanie.

Ďalšia otázka, ktorá vyvstáva v súvislosti s cyklickým tlmivým roztokom. Musím vypísať nové údaje alebo prepísať existujúce, keď sú plné? Odborníci tvrdia, že neexistuje jasná výhoda jedného oproti druhému a jeho implementácia závisí od konkrétnej situácie. Ak majú tieto aplikácie väčší význam pre aplikáciu, použite metódu prepísania. Na druhej strane, ak sú spracované v režime prvý príde prvý, potom sa nové zahodia, keď je prstencová vyrovnávacia pamäť plná.

Implementácia cyklického frontu

Začíname s implementáciou, definujte typy údajov a potom metódy: core, push a pop. V postupoch "push" a "pop" sa body posunu "next" vypočítajú pre miesto, kde dôjde k aktuálnemu zápisu a čítaniu. Ak ďalšie miesto ukazuje na chvost, potom je vyrovnávacia pamäť plná a údaje sa už nezapisujú. Podobne, keď sa "hlava" rovná "chvostu", je prázdna a nič sa z nej nečíta.

Implementácia cyklického frontu

Štandardný prípad použitia

Pomocný postup sa nazýva procesom aplikácie na extrahovanie údajov z medzipamäte Java ring. Mal by byť zahrnutý do kritických častí, ak kontajner číta viac ako jedno vlákno. Chvost sa pred načítaním informácií presunie na ďalší posun, pretože každý blok je jeden bajt a podobné množstvo sa rezervuje vo vyrovnávacej pamäti, keď je zväzok úplne načítaný. Ale v pokročilejších implementáciách cyklického disku nemusia mať jednotlivé oddiely nevyhnutne rovnakú veľkosť. V takýchto prípadoch sa snažia uložiť aj posledný bajt pridaním ďalších kontrol a hraníc.

V takýchto schémach, ak sa chvost pohybuje pred čítaním, informácie, ktoré je potrebné prečítať, môžu byť potenciálne prepísané novo rozšírenými údajmi. Vo všeobecnosti sa odporúča najskôr prečítať a potom posunúť ukazovateľ chvosta. Najprv určte dĺžku vyrovnávacej pamäte a potom vytvorte inštanciu "circ_bbuf_t" a priraďte ukazovateľ "maxlen". V tomto prípade musí byť kontajner globálny alebo umiestnený na stohu. Napríklad, ak potrebujete 32-bajtovú prstencovú vyrovnávaciu pamäť, vykonajte v aplikácii nasledujúce kroky (pozri. obrázok nižšie).

Štandardný prípad použitia

Špecifikácia funkčných požiadaviek

Dátový typ "ring_t" bude dátový typ, ktorý obsahuje ukazovateľ na vyrovnávaciu pamäť, jeho veľkosť, index hlavičky a chvosta, počítadlo údajov.

Inicializačná funkcia "ring_init ()" inicializuje vyrovnávaciu pamäť na základe získania ukazovateľa na štruktúru kontajnera vytvoreného volajúcou funkciou s preddefinovanou veľkosťou.

Funkcia pridania hovoru " ring_add ()" pridá bajt na ďalšie dostupné miesto vo vyrovnávacej pamäti.

Funkcia odstránenia krúžku "ring_remove ()" odstráni bajt z najstaršieho platného miesta v kontajneri.

Funkcia Ring peek vo funkcii" ring_peek () "načíta počet bajtov "uint8_t ` count`" z medzipamäte ring na nový poskytnutý ako parameter bez odstránenia akýchkoľvek hodnôt načítaných z kontajnera. Vráti počet skutočne prečítaných bajtov.

Funkcia čistenia zvonenia "ring_clear ()" nastaví "Tail" na "Head" a načíta "0" do všetkých polôh vyrovnávacej pamäte.

Vytvorenie vyrovnávacej pamäte v C / C ++

Kvôli obmedzeným zdrojom vstavaných systémov možno dátové štruktúry s cyklickou vyrovnávacou pamäťou nájsť vo väčšine projektov s pevnou veľkosťou, ktoré fungujú, akoby pamäť bola vo svojej podstate kontinuálna a cyklická. Údaje nie je potrebné preskupovať, pretože sa generuje a používa pamäť a upravujú sa ukazovatele hlavy/chvosta. Pri vytváraní knižnice cyklických vyrovnávacích pamätí musia používatelia pracovať s rozhraniami API knižnice a priamo meniť štruktúru. Preto je zapuzdrenie prstencového pufra v "C sa používa". Týmto spôsobom vývojár ponechá implementáciu knižnice a podľa potreby ju zmení bez toho, aby ju museli aktualizovať aj koncoví používatelia.

Používatelia nemôžu pracovať s ukazovateľom "circular_but_t", vytvorí sa typ deskriptora, ktorý je možné použiť namiesto toho. Tým sa eliminuje potreba vrhať ukazovateľ do implementácie.funkcie "typedefcbuf_handle_t" . Vývojári musia vytvoriť API pre knižnicu. Interagujú s knižnicou medzipamäte" C " pomocou nepriehľadného typu deskriptora, ktorý sa vytvorí počas inicializácie. Ako základný typ údajov zvyčajne vyberte "uint8_t" . Môžete však použiť akýkoľvek konkrétny typ, pričom dbajte na správne spracovanie základnej vyrovnávacej pamäte a počtu bajtov. Používatelia interagujú s kontajnerom vykonaním povinných postupov:

  1. Inicializujte kontajner a jeho veľkosť.
  2. Resetujte kruhový kontajner.
  3. Pridajte údaje do medzipamäte zvonenia na "Si".
  4. Získajte nasledujúcu hodnotu z kontajnera.
  5. Vyžiadajte si informácie o aktuálnom počte prvkov a maximálnej kapacite.
Resetujte kruhový kontajner

"Plné "aj" prázdne " prípady vyzerajú rovnako: "hlava" a "chvost", ukazovatele sú rovnaké. Existujú dva prístupy, ktoré rozlišujú medzi plným a prázdnym:

  1. Plný stav chvost + 1 = = hlava.
  2. Prázdny stav hlava = = chvost.

Implementácia knižničných funkcií

Ak chcete vytvoriť kruhový kontajner, použite jeho štruktúru na správu štátu. Aby sa zachovalo zapuzdrenie, štruktúra je definovaná vo vnútri knižnice ".C " súbor, nie v hlavičke. Počas inštalácie budete musieť sledovať:

  1. Base data buffer.
  2. Maximálna veľkosť.
  3. Aktuálna poloha hlavy, ktorá sa po pridaní zvyšuje.
  4. Súčasný chvost, ktorý sa zvyšuje, keď je odstránený.
  5. Vlajka označujúca, či je kontajner plný alebo nie.

Teraz, keď je kontajner navrhnutý, sú implementované funkcie knižnice. Každé z rozhraní API vyžaduje inicializovaný deskriptor vyrovnávacej pamäte. Namiesto upchávania kódu podmienenými príkazmi použite príkazy, aby ste sa uistili, že požiadavky API sú splnené v štýle.

Požiadavky na štýl API

Implementácia nebude zameraná na vlákna, ak zámky neboli pridané do základnej knižnice cyklických úložísk. Na inicializáciu kontajnera má API klientov, ktorí poskytujú základnú veľkosť vyrovnávacej pamäte, takže ju vytvárajú na strane knižnice, napríklad pre jednoduchosť "malloc". Systémy, ktoré nemôžu používať dynamickú pamäť, musia zmeniť funkciu "init", aby používali inú metódu, napríklad alokáciu zo statického kontajnerového fondu.

Ďalším prístupom je prerušenie zapuzdrenia, ktoré umožňuje používateľom staticky deklarovať Kontajnerové štruktúry. V takom prípade je potrebné aktualizovať "circular_buf_init", aby ste získali ukazovateľ alebo "init", vytvorili štruktúru zásobníka a vrátili ho. Keďže je však zapuzdrenie prerušené, používatelia ho budú môcť zmeniť bez postupov knižnice. Po vytvorení kontajnera vyplňte hodnoty a zavolajte "obnoviť". Pred návratom z "init" systém zabezpečí, aby bol kontajner vytvorený v prázdnom stave.

Kontajner bol vytvorený v prázdnom stave

Pridávanie a mazanie údajov

Pridávanie a mazanie údajov z vyrovnávacej pamäte vyžaduje manipuláciu s ukazovateľmi "hlava" a "chvost". Po pridaní do kontajnera sa do aktuálneho "hlava"-miesto a propagujte ho. Po odstránení získajte hodnotu aktuálneho "chvost"-ukazovateľ a podporovať "chvost". Ak potrebujete podporiť "chvost"-ukazovateľ, ako aj "hlava", je potrebné skontrolovať, či vloženie hodnoty spôsobuje "plný". Keď je vyrovnávacia pamäť už plná, posuňte "chvost" o krok pred "hlavu".

Pridávanie a mazanie údajov

Po povýšení ukazovateľa vyplňte "plný"-vlajka, kontrola rovnosti "hlava = = chvost". Modulárne použitie operátora spôsobí, že "hlava" a "chvost" resetujú hodnoty na "0", keď sa dosiahne maximálna veľkosť. To zaisťuje, že "hlava" a "chvost" budú vždy platnými indexmi základného dátového kontajnera: "static void advance_pointer (cbuf_handle_t cbuf)". Môžete vytvoriť podobnú pomocnú funkciu, ktorá sa volá pri odstraňovaní hodnoty z vyrovnávacej pamäte.

Rozhranie Triedy Šablón

Aby implementácia C++ podporovala akékoľvek dátové typy, spustite šablónu:

  1. Resetovanie vyrovnávacej pamäte na čistenie.
  2. Pridávanie a mazanie údajov.
  3. Kontrola úplného / prázdneho stavu.
  4. Kontrola aktuálneho počtu položiek.
  5. Kontrola celkovej kapacity kontajnera.
  6. Aby ste po zničení vyrovnávacej pamäte nezanechali žiadne údaje, Použite Inteligentné ukazovatele C++, aby ste zaistili, že používatelia môžu údaje spravovať.
Rozhranie Triedy Šablón

V tomto príklade vyrovnávacia pamäť C++ napodobňuje väčšinu logiky implementácie C, ale výsledkom je oveľa čistejší a opakovane použiteľný dizajn. Okrem toho kontajner C++ používa "std:: mutex" poskytnúť implementáciu zameranú na vlákna. Pri vytváraní triedy prideľujú údaje pre hlavnú vyrovnávaciu pamäť a nastavujú jej veľkosť. Tým sa eliminuje réžia potrebná pri implementácii C. Naproti tomu konštruktor C++ nevolá "obnoviť", pretože sú zadané počiatočné hodnoty pre členské premenné, kruhový kontajner sa spustí v správnom stave. Resetovacie správanie vráti vyrovnávaciu pamäť do prázdneho stavu. V implementácii cyklického kontajnera C++ hlásia "veľkosť" a "kapacita" počet položiek vo fronte, nie veľkosť v bajtoch.

Ovládač UART STM32

Po spustení vyrovnávacej pamäte musí byť integrovaná do ovládača UART. Najprv ako globálny prvok v súbore, takže musíte deklarovať:

  • "deskriptor_rbd" a vyrovnávacia pamäť "_rbmem: statický rbd_t _rbd";
  • "statický znak _rbmem [8]".

Pretože sa jedná o ovládač UART, kde každý znak musí byť 8-bitový, je prijateľné vytvoriť pole znakov. Ak sa použije 9-alebo 10-bitový režim, musí byť každý prvok "uint16_t". Kontajner sa vypočíta tak, aby sa zabránilo strate údajov.

Moduly frontu často obsahujú štatistické informácie, ktoré vám umožňujú sledovať maximálne využitie. V inicializačnej funkcii "uart_init" musí byť vyrovnávacia pamäť inicializovaná volaním "ring_buffer_init" a odovzdaním štruktúry atribútov každému členovi, ktorému sú priradené diskutované hodnoty. Ak sa úspešne inicializuje, modul UART sa odstráni z resetu, prerušenie príjmu je povolené v IFG2.

Ovládač UART STM32

Druhá funkcia, ktorú je potrebné zmeniť, je "uart_getchar". , čítanie prijatého symbolu z periférneho zariadenia UART sa nahradí čítaním z frontu. Ak je fronta prázdna, funkcia by mala vrátiť -1. Ďalej musíte implementovať UART, aby ste získali ISR. Otvorte súbor hlavičky "msp430g2553.h", posuňte sa nadol do sekcie vektor prerušenia, kde nájdu vektor s názvom USCIAB0RX. Pomenovanie naznačuje, že ho používajú moduly USCI A0 a B0. Stav prerušenia príjmu USCI A0 je možné prečítať z IFG2. Ak je nastavený, príznak by sa mal vymazať a údaje v prijímacom priestore sa uložia do vyrovnávacej pamäte pomocou "ring_buffer_put".

Úložisko údajov UART

Toto úložisko poskytuje informácie o tom, ako čítať údaje UART pomocou DMA, keď počet bajtov, ktoré sa majú prijať, nie je vopred známy. V rodine mikrokontrolérov môže prstencová vyrovnávacia pamäť STM32 pracovať v rôznych režimoch:

  1. Režim hlasovania (bez DMA, bez IRQ) - aplikácia musí zisťovať stavové bity, aby skontrolovala, či bol prijatý nový znak, a prečítať ho dostatočne rýchlo, aby získala všetky bajty. Veľmi jednoduchá implementácia, ale nikto ju nepoužíva v reálnom živote. Nevýhody - je ľahké preskočiť prijaté znaky v dátových paketoch, funguje len pri nízkych prenosových rýchlostiach.
  2. Režim prerušenia ( bez DMA) - medzipamäť zvonenia UART spustí prerušenie a procesor sa prepne na obslužný program na spracovanie príjmu údajov. Najčastejšie otázky prístup vo všetkých aplikáciách dnes funguje dobre v rozsahu stredných otáčok. Nevýhody-postup spracovania prerušenia sa vykonáva pre každý prijatý symbol, môže zastaviť ďalšie úlohy vo vysoko výkonných mikrokontroléroch s veľkým počtom prerušení a súčasne v operačnom systéme pri prijímaní dátového paketu.
  3. , Režim DMA sa používa na prenos údajov z registra USART RX do užívateľskej pamäte na hardvérovej úrovni. V tejto fáze nie je potrebná interakcia s aplikáciou, s výnimkou potreby spracovania údajov prijatých aplikáciou. Môže pracovať veľmi ľahko s operačné systémy. Optimalizované pre vysoké rýchlosti prenosu dát > 1Mbps a nízkoenergetické aplikácie, v prípade veľkých dátových paketov môže zväčšenie veľkosti vyrovnávacej pamäte zlepšiť funkčnosť.

Implementácia v Arduine

Prstencová vyrovnávacia pamäť Arduino sa vzťahuje tak na dizajn dosiek, ako aj na použité programovacie prostredie pracovať. Jadrom Arduina je mikrokontrolér série Atmel AVR. Je to AVR, ktorý robí väčšinu práce a v mnohých ohľadoch doska Arduino okolo AVR predstavuje funkčnosť-ľahko pripojiteľné kontakty, USB-sériové rozhranie pre programovanie a komunikáciu.

Mnoho bežných dosiek Arduino v súčasnosti používa prstencový buffer ATmega 328, staršie dosky používali ATmega168 a ATmega8. Dosky ako Mega si vyberajú zložitejšie možnosti, ako napríklad 1280 a podobné. Čím rýchlejšie splatné a nula, tým lepšie použitie ARM. Existuje asi tucet rôznych dosiek Arduino s názvami. Môžu mať rôzne množstvá flash pamäte, RAM a I / O portov s medzipamäťou AVR ring.

AVR ring Buffer

Používa sa premenná "roundBufferIndex" na skladovanie aktuálnu pozíciu a po pridaní do vyrovnávacej pamäte.

, pole bude obmedzené obmedzeniami poľa

Toto sú výsledky vykonávania kódu. Čísla sa ukladajú do vyrovnávacej pamäte a keď sú plné, začnú sa prepisovať. Týmto spôsobom môžete získať posledné n čísla.

Posledné N čísla

V predchádzajúcom príklade bol index použitý na prístup k aktuálnej pozícii vyrovnávacej pamäte, pretože postačuje na vysvetlenie operácie. Ale vo všeobecnosti je normálne, že sa používa ukazovateľ. Toto je upravený kód na použitie ukazovateľa namiesto indexu. V skutočnosti je operácia rovnaká ako predchádzajúca a výsledky sú podobné.

Vysoko výkonné operácie CAS

Vysoko výkonné operácie CAS

Disruptor je vysoko výkonná knižnica na prenos správ medzi vláknami, vyvinutá a otvorená pred niekoľkými rokmi spoločnosťou Lmax Exchange. Vytvorili tento softvér na zvládnutie obrovskej prevádzky (viac ako 6 miliónov TPS) na svojej maloobchodnej finančnej obchodnej platforme. V roku 2010 všetkých prekvapili tým, ako rýchlo by mohol byť ich systém spustením celej obchodnej logiky v jednom vlákne. Aj keď jedno vlákno bolo pri ich riešení dôležitým konceptom, Disruptor pracuje vo viacvláknovom prostredí a je založený na kruhovej vyrovnávacej pamäti - toku, v ktorom už nie sú potrebné zastarané údaje, pretože prichádzajú novšie a relevantnejšie údaje.

V takom prípade bude fungovať vrátenie falošnej Boolovskej hodnoty alebo blokovanie. Ak žiadne z týchto riešení neuspokojí používateľov, je možné implementovať vyrovnávaciu pamäť s premenlivou veľkosťou, ale iba vtedy, keď je naplnená, a to nielen vtedy, keď výrobca dosiahne koniec poľa. Zmena veľkosti bude vyžadovať presun všetkých prvkov do novo prideleného väčšieho poľa, ak sa použije ako základná dátová štruktúra, čo je samozrejme nákladná operácia. Je ich veľa ďalšie veci, vďaka ktorým je Disruptor rýchly, napríklad konzumácia správ v dávkovom režime.

Prstencový buffer "qtserialport" (sériový port) je zdedený z QIODevice, môže byť použitý na príjem rôznych sériových informácií a zahŕňa všetky dostupné sériové zariadenia. Sériový port je vždy otvorený v exkluzívnom prístupe, čo znamená, že iné procesy alebo vlákna nemajú prístup k otvorenému sériovému portu.

Prstencové nárazníky sú veľmi užitočné v "C programovanie", , môžete napríklad vyhodnotiť tok bajtov prichádzajúcich cez UART.

Články na tému