Какво е пръстен буфер?

Пръстен буфер е известен също като опашка или цикличен буфер и е обичайна форма на опашка. Това е популярен, лесно приложим стандарт и въпреки че е представен като кръг, той е линеен в основния код. Опашката на пръстена съществува като масив с фиксирани дължини с две указатели: едната представлява началото на опашката, а другата - на опашката. Недостатъкът на метода е неговият фиксиран размер. За опашки, в които елементите трябва да бъдат добавени или премахнати по средата, а не само в началото и в края на буфера, изпълнението като свързан списък е най-добрият подход.

Теоретична основа на буфера

За потребителя е по-лесно да избере ефективна структура на масивите, след като разбере фундаменталната теория. Цикъл буферът е структура от данни, където масивът се обработва и визуализира под формата на контури, т.е. индексите се връщат на 0 след достигане на дължината на масива. Това се прави с две указатели на масива: "глава" и "опашка". Когато данните се добавят към буфера, индексът на заглавието се придвижва нагоре. По същия начин, когато се отстраняват, опашката също се движи нагоре. Определянето на главата, опашката, посоката на движение, мястото на записване и четене зависят от изпълнението на схемата.


Циркулярните буфери се използват прекалено ефективно за решаване на потребителски проблеми. Това означава, че един поток на изпълнение е отговорен за производството на данни, а другият за потребление. При вградени устройства с много ниско и средно ниво, производителят е представен във формат ISR (информация, получена от сензори), а потребителят - във форматаосновния цикъл на събитието. Характеристиката на цикличните буфери е, че те се изпълняват без необходимост от заключване в околната среда на един производител и един потребител. Това ги прави идеална информационна структура за вградени приложения. Следващата разлика е, че няма точен начин да се разграничи запълненият сектор от празния. Това е така, защото и в двата случая главата се слива с опашката. Има много начини и средства за справяне с него, но повечето от тях го правят по-объркващо и усложнява четимостта.


Друг въпрос, който възниква във връзка с цикличния буфер. Необходимо е да възстановите новите данни или да запишете повторно съществуващите, когато е пълно? Специалистите твърдят, че няма очевидно предимство един над друг и неговото прилагане зависи от конкретната ситуация. Ако последните са по-подходящи за приложението, използвайте метода rewrite. От друга страна, ако се обработват в режим "първи дошъл - първи обслужен", тогава се отхвърлят нови, когато се запълни пръстеновиден буфер.

Реализация на циклична опашка

Когато става въпрос за изпълнение, типовете данни се дефинират и след това методите са: ядро, push и pop. В процедурите "push" и "pop" изчислете "такива" точки на смяна за мястото, в което ще се проведе текущият запис и четене. Ако това местоположение сочи към опашката, тогава буферът е пълен и данните не се записват. По същия начин, когато "главата" е равна на "опашката", тя е празна и нищо не се чете от нея.

Стандартна версия на употребата

Спомагателната процедура се нарича от програмния процесза извличане на данни от Java циркулярния буфер. Тя трябва да бъде включена в критични секции, ако контейнерът чете повече от една нишка. Опашката се премества към следващата смяна, преди информацията да бъде прочетена, тъй като всеки блок е един байт и запазва същото количество в буфера, когато обемът е напълно зареден. Но при по-усъвършенствани реализации на циклично задвижване, отделните секции не е задължително да са с еднакъв размер. В такива случаи те се опитват да запазят дори последния байт, добавяйки още чекове и граници. В такива схеми, ако опашката се движи преди четене, информацията, която трябва да бъде прочетена, може потенциално да бъде презаписана от новите предадени данни. По принцип се препоръчва първо да се прочете, а след това да се премести посоката на опашката. Първо определете дължината на буфера и след това създайте копие на "circ_bbuf_t" и задайте показалец на "maxlen". В този случай, контейнерът трябва да бъде глобален или подреден. Например, ако се нуждаете от 32-байтов пръстен буфер, изпълнете следното в прикачения файл (вижте картинката по-долу).

Спецификация на функционалните изисквания

Типът данни „ring_t“ ще бъде тип данни, който съдържа указател към буфера, неговия размер, индекса на заглавката и опашката и брояч на данни. Функцията на инициализация "ring_init ()" инициализира буфера въз основа на получаването на указател към структурата на контейнера, създаден от функцията за повикване с определен размер. Функцията ring_add () добавя байт към следващото свободно пространство в буфера.Функцията за премахване на ring_remove () ще изтрие байт от най-старото налично пространство в контейнера. Визията на пръстена в функцията ring_peek () ще прочете броя на байтовете "uint8_t" брой "от пръстенния буфер към новия, предоставен като параметър, без да премахва всички стойности от контейнера. Ще се върне броят на действително прочетените байтове.
Функцията ring_clear () ще зададе "Tail" равна на "Head" и ще зареди "0" във всички буферни позиции.

Създаване на буфер в C /C ++

Поради ограничените ресурси на вградените системи, структурата на данните с цикличен буфер може да бъде намерена в повечето проекти с фиксиран размер, които работят така, сякаш паметта по природа е непрекъсната и пръстен. Не е необходимо да пренареждате данните, защото паметта се генерира и използва, а индикатори за глава /опашка се коригират. Когато създавате библиотека с цикличен буфер, потребителите трябва да работят с библиотеки API, вместо да променят структурата директно. Затова се използва капсулиране на пръстенния буфер на "С". По този начин разработчикът ще спаси реализацията на библиотеката, като я промени, ако е необходимо, без да изисква от крайните потребители да го актуализират.

Потребителите не могат да работят с указател "circular_but_t", като създават тип дескриптор, който може да се използва вместо това. Това елиминира необходимостта от преместване на курсора за реализиране на функцията .typedefcbuf_handle_t. Разработчиците трябва да създадат API за библиотеката. Те взаимодействат с библиотеката на кръговия буфер "С", като използват непрозрачен тип дескриптор,който се създава по време на инициализацията. Обикновено избирате "uint8_t" като основен тип данни. Но можете да използвате всеки конкретен тип, като внимавате да управлявате правилно основния буфер и броя на байтовете. Потребителите взаимодействат с контейнера, следвайки необходимите процедури:
  • Инициализирайте контейнера и неговия размер.
  • Нулирайте кръговия контейнер.
  • Да се ​​добавят данни към буферния пръстен при "С".
  • Получете следната стойност от контейнера.
  • Искане на информация за текущия брой артикули и максималния капацитет.
  • И "пълни", и "празни" случаи изглеждат еднакви: "главата" и "опашката", указателите са равни. Има два подхода, които правят разлика между пълно и празно:
  • Пълна държавна опашка + 1 == глава.
  • Празна държавна глава == опашка.
  • Изпълнение на библиотечни функции

    За да се създаде кръгов контейнер, използвайте неговата структура, за да контролирате състоянието. За да запишете капсулацията, структурата се дефинира в библиотечния .c файл, а не в заглавната част. Когато инсталирате, ще трябва да проследите:
  • Основен буфер за данни.
  • Максимален размер.
  • Настоящото положение на главата се увеличава с добавянето.
  • Текуща опашка, увеличава се, когато се отстранява.
  • Флаг, показващ пълния контейнер или не.
  • Сега, когато контейнерът е проектиран, той изпълнява библиотечни функции. Всеки API изисква инициализиран дескриптор на буфер. Вместо да запуши кода с условни изрази, кандидатствайте за одобрение, за да приложите изискваниятаAPI в стил.
    Реализацията няма да бъде ориентирана към ток, ако в основната библиотека на цикличните хранилища не е добавена никаква блокировка. За API инициализация, клиентите, които осигуряват размера на основния буфер, затова го създават отстрани на библиотеката, например, за да опростят "malloc". Системите, които не могат да използват динамичната памет, трябва да променят функцията "init", за да използват друг метод, като например избор от статичен пул на контейнери. Друг подход е да се прекъсне капсулирането, което позволява на потребителите да декларират статично контейнерни структури. В този случай, "circular_buf_init" трябва да бъде актуализиран, за да вземе указател или "init", да създаде структура на стека и да го върне. Въпреки това, тъй като капсулирането е нарушено, потребителите могат да го променят без библиотечни процедури. След като контейнерът е създаден, попълнете стойността и причинете "нулиране". Преди да се върнете от "init", системата гарантира, че контейнерът е в празно състояние.

    Добавяне и изтриване на данни

    Добавянето и премахването на данни от буфера изисква манипулиране от индикаторите "главата" и "опашката". Когато се добави към контейнера, вмъкнете нова стойност в текущите "главни" сайтове и го популяризирайте. Когато бъдат премахнати, те получават стойността на текущата "опашка" -индикатор и насърчават "опашката". Ако искате да преместите индекса "tail", а също и "head", трябва да проверите дали вмъкването причинява стойността "full". Когато буферът вече е пълен, преместете "опашката" една стъпка напред "глава".
    След като показалеца е разгърнат, попълнете "пълното" -издаване,проверка на равенството "глава == опашка". Модулното използване на оператора ще доведе до нулиране на "главата" и "опашката" до "0" при достигане на максималния размер. Това гарантира, че "главата" и "опашката" винаги ще бъдат валидни бази данни на основния контейнер за данни: "static void advance_pointer (cbuf_handle_t cbuf)". Можете да създадете подобна допълнителна функция, която се извиква при изтриване на стойности от буфера.

    Шаблонен интерфейс

    За да може С ++ изпълнението да поддържа всеки тип данни, следвайте шаблона:
  • Нулирайте буфера за почистване.
  • Добавяне и изтриване на данни.
  • ​​
  • Проверка на пълното /празното състояние.
  • Проверка на текущия брой позиции.
  • Проверка на общия капацитет на контейнера.
  • За да не се оставят никакви данни след унищожаването на буфера, се използват интелигентни C ++ указатели, за да се гарантира, че потребителите могат да управляват данните.
  • В този пример буферът C ++ симулира по-голямата част от логиката на реализацията на С, но води до много по-чист и многократно използваем дизайн. В допълнение, C ++ контейнерът използва "std :: mutex" за осигуряване на текущо-ориентирано изпълнение. Когато създавате клас, изберете данни за главния буфер и задайте неговия размер. Това елиминира изискваното от изпълнението на C. режима. За разлика от него, конструкторът C ++ не причинява "нулиране", тъй като определя първоначалните стойности на членовете, циркулярният контейнер се изпълнява в правилното състояние. Поведението при нулиране връща буфера в празно състояние. При прилагането на C ++ "размер" цикличен контейнер и"Капацитет" отчита броя на елементите в опашката, а не размера им в байтове.

    UART STM32 драйвер

    След като буферът е стартиран, той трябва да бъде интегриран в UART драйвера. Първо като глобален елемент във файла е необходимо да се декларират:
  • "descriptor_rbd" и буферна памет "_rbmem: static rbd_t _rbd";
  • "статичен символ _ 8".
  • Тъй като това е UART драйвер, където всеки знак трябва да бъде 8-битов, създаването на масив от символи е допустимо. Ако се използва 9 или 10 битов режим, всеки елемент трябва да бъде "uint16_t". Контейнерът се изчислява по такъв начин, че да се избегне загуба на данни. Често, модулите на опашката съдържат статистическа информация, която позволява проследяване на максималната употреба. В инициализиращата функция "uart_init", буферът трябва да бъде инициализиран чрез извикване на "ring_buffer_init" и прехвърляне на структурата на атрибута към всеки член, на който са зададени договорените стойности. Ако е успешно инициализиран, модулът UART се извежда от нулирането, приемането на прекъсване е позволено в IFG2.
    Втората функция, която трябва да се промени, е "uart_getchar". Четенето на получения знак от периферното устройство на UART се заменя с четене от опашката. Ако опашката е празна, функцията трябва да върне -1. След това трябва да приложите UART, за да получите ISR. Отворете заглавния файл "msp430g2553.h", превъртете надолу до секцията на прекъсващите вектори, където се намира векторът с името USCIAB0RX. Именуването означава, че се използва от модули USCI A0 и B0. Състоянието на прекъсване на приемането на USCI A0 може да бъде прочетено от IFG2. Ако е инсталиран, флагът трябва да бъде изчистен и данните в приемното отделение се поставят в буфераизползвайки "ring_buffer_put".

    UART Data Store

    Това хранилище предоставя информация за това как да се четат UART данни, използвайки DMA, когато броят на байтовете, които се получават предварително, е неизвестен. Семейството STM32 кръгъл буфер може да работи в различни режими:
  • Режим на запитване (без DMA, без IRQ) - приложението трябва да тества бита за състоянието, за да провери дали е приет нов символ и да го прочете достатъчно бързо, за да получи всички байтове. Много просто изпълнение, но никой не го използва в реалния живот. Против - лесно е да се пропуснат получените символи в пакетите с данни, работи само при ниски скорости на предаване.
  • Режим на прекъсване (без DMA) - пръстенният буфер UART прекъсва прекъсването и CPU преминава към сервизната програма за обработка на приемането на данни. Най-разпространеният подход във всички приложения днес, работи добре в средния диапазон. Против - процедурата за прекъсване се извършва за всеки получен символ, той може да спре други задачи във високопроизводителните микроконтролери с голям брой прекъсвания и едновременно с операционната система при получаване на пакет данни.
  • Режимът DMA се използва за прехвърляне на данни от USART RX регистъра към хардуерно ниво. На този етап не се изисква взаимодействие с приложението, с изключение на необходимостта от обработка на данните, получени от приложението. Тя може да бъде много лесна за работа с операционните системи. Оптимизиран за високи скорости на данните & gt; 1Mbps и приложения с ниска мощност, в случай на големи пакети данни, увеличаването на размера на буфера може да се подобрифункционалност.
  • Изпълнение на ARDUINO

    Буферът на лентата Arduino се отнася както до дизайна на дъските, така и до програмната среда, използвана за работа. Ядрото на Arduino е микроконтролерът Atmel AVR. Това е AVR, което изпълнява по-голямата част от работата, и в много отношения дъската Arduino около AVR осигурява функционалност - лесно включване на контакти, USB-сериен интерфейс за програмиране и комуникация. Много от редовните карти на Arduino в момента използват пръстен буфер с ATmega 328, по-старите дънни платки използваха ATmega168 и ATmega8. Таблата като Mega избират по-сложни опции като 1280 и други подобни. Колкото по-бързо дължимата и нулата, толкова по-добро използване на ARM. Има около десет различни Arduino табла с имена. Те могат да имат различно количество флаш памет, RAM и I /O портове с AVR буфер.
    Променливата "roundBufferIndex" се използва за съхраняване на текущата позиция и при добавяне към буфера ще има ограничение на масив.
    Това са резултатите от изпълнението на кода. Числата се съхраняват в буфера и когато са пълни, започват да пренаписват. Така че можете да получите последните N номера.
    В предишния пример индексът е използван за достъп до текущата позиция на буфера, тъй като е достатъчно да се обясни операцията. Но като цяло е нормално да използвате указател. Това е модифициран код за използване на показалец вместо индекс. По същество същата операция като предишната и получените резултати са сходни.

    Операции със СС с висока ефективност

    Разрушителят еВисокопроизводителна библиотека за стрийминг съобщения, разработена и отворена преди няколко години от LMAX Exchange. Те създадоха този софтуер, за да се справят с огромен трафик (повече от 6 милиона TPS) в своята търговска платформа за търговия на дребно. През 2010 г. те изненадаха всички, колко бързо може да бъде тяхната система, след като завършили цялата бизнес логика в една нишка. Въпреки че една нишка е важна концепция в тяхното решение, Disruptor работи в многонишкова среда и се основава на пръстен буфер - поток, в който остарелите данни вече не са необходими, тъй като става по-свеж и по-актуален. В този случай ще се върне или фалшивата логическа стойност или заключването. Ако нито едно от тези решения не удовлетвори потребителите, може да се приложи преоразмерителен буфер, но само когато е запълнен, а не само когато производителят достигне края на масива. Промяната на размера ще изисква преместване на всички елементи към ново разпределен масив, ако се използва като основна структура на данните, което разбира се е скъпа операция. Има много други неща, които правят Disruptor един бърз, като например съобщения в пакетния режим. Цифровият буфер qtserialport (сериен порт) се наследява от QIODevice, може да се използва за получаване на различна серийна информация и включва всички налични серийни устройства. Серийният порт е винаги отворен при монополен достъп, което означава, че други процеси или нишки не могат да имат достъп до отворения сериалпристанището Пръстеновите буфери са много полезни при програмирането за "С", например, можете да оцените потока от байтове, идващи през UART.

    Свързани публикации