Перейти к содержанию

Потоковый буфер (stream buffer)

Прерывание UART или DMA отдаёт байты пачками разной длины, а задача хочет спать, пока не накопится целая посылка. Очередь FreeRTOS для этого тяжеловата, кольцевой буфер не умеет будить задачу. Потоковый буфер — компромисс между ними.

Обновлено Редакция GAW

Потоковый буфер (stream buffer) — примитив FreeRTOS для передачи непрерывного потока байтов от одного писателя одному читателю: из обработчика прерывания в задачу, из задачи в задачу или между ядрами двухъядерного процессора. Данные копируются: отправитель кладёт байты в буфер, читатель забирает их оттуда. Читатель может заблокироваться и не тратить процессорное время, пока в буфере не накопится заданное число байтов. Поверх потокового буфера построен message-буфер (message buffer), который передаёт не поток, а дискретные сообщения переменной длины.

Правило «один писатель — один читатель»

Это единственный объект FreeRTOS, который не защищён от нескольких одновременных писателей или читателей. Писатель и читатель могут быть разными задачами или прерываниями, но если писателей несколько, вызовы xStreamBufferSend() нужно сериализовать самим; то же с xStreamBufferReceive() для читателей. Документация предлагает выполнять вызов внутри критической секции с временем блокировки 0. Из прерывания пишут через xStreamBufferSendFromISR(). Для пробуждения читателя буфер использует уведомление задачи с индексом 0, поэтому собственные уведомления в задаче-читателе должны занимать другие индексы.

Уровень срабатывания

Буфер создаётся вызовом xStreamBufferCreate(xBufferSizeBytes, xTriggerLevelBytes) (или xStreamBufferCreateStatic() с собственным массивом). Второй параметр — уровень срабатывания: сколько байтов должно быть в буфере, чтобы заблокированный на чтении читатель проснулся.

УровеньПоведение читателя
1просыпается от каждого байта
10ждёт, пока накопится не меньше 10 байт
0заменяется на 1
больше размера буферанедопустимо

Если время блокировки истекает раньше, чем набрался уровень, читатель всё равно получает те байты, что есть. Так одна задача может разбирать посылки по UART, просыпаясь не на каждый байт, а на заголовок известной длины.

С чем не путать

  • Очередь RTOS хранит элементы фиксированного размера и допускает много писателей и читателей; потоковый буфер легче, но рассчитан на пару «один — один» и работает с байтами произвольными порциями.
  • Кольцевой буфер — структура данных без участия ядра: он не умеет блокировать читателя и будить его по уровню, зато не требует RTOS вовсе. Для чистого «голого» прерывания UART это обычный выбор, см. статью «Кольцевые буферы и UART».
  • DMA переносит данные без процессора, но ему всё равно нужен буфер, из которого задача заберёт результат; потоковый буфер здесь годится как связка «прерывание завершения DMA — задача».

В заголовке stream_buffer.h объявлен и третий тип — батч-буфер (sbTYPE_STREAM_BATCHING_BUFFER) с тем же параметром уровня срабатывания. Поддержка потоковых буферов включена по умолчанию: configUSE_STREAM_BUFFERS равно 1, если не определено. Общие правила обмена между прерыванием и задачей — в статье «Прерывания и параллельность».

Частые вопросы

Чем потоковый буфер отличается от очереди FreeRTOS?

Очередь хранит элементы фиксированного размера и допускает сколько угодно писателей и читателей. Потоковый буфер передаёт непрерывный поток байтов произвольными порциями и рассчитан ровно на одного писателя и одного читателя — за счёт этого его реализация легче. Если писателей несколько, их вызовы xStreamBufferSend() придётся сериализовать самостоятельно, например в критической секции с нулевым временем блокировки. Данные в обоих случаях копируются.

Что такое уровень срабатывания (trigger level)?

Число байтов, которое должно накопиться в буфере, чтобы заблокированный читатель проснулся. При уровне 1 задача просыпается от каждого байта, при уровне 10 — когда в буфере не меньше 10 байт. Если время блокировки истекло раньше, задача всё равно получает те байты, что есть. Уровень 0 заменяется на 1, а уровень больше размера буфера задавать нельзя.

Чем message-буфер отличается от потокового?

Message-буфер передаёт дискретные сообщения переменной длины: читатель получает ровно то сообщение, что было записано, а не произвольный кусок потока. Он построен поверх потокового буфера, поэтому наследует его правило «один писатель — один читатель» и тоже копирует данные.

Источники

  1. FreeRTOS. FreeRTOS Kernel documentation: Stream and message buffers (Kernel features), 2026 — разд. Introduction: примитивы для связи задача — задача и прерывание — задача, оптимизированы под одного читателя и одного писателя — например, из обработчика прерывания в задачу или между ядрами двухъядерного процессора; данные копируются в буфер отправителем и из буфера читателем; потоковый буфер передаёт непрерывный поток байтов, message-буфер — дискретные сообщения переменной длины и построен поверх потокового; важное примечание: единственный объект FreeRTOS, где допустимы только один писатель и один читатель (разные задачи или прерывания — можно, несколько писателей или читателей — нельзя); при нескольких писателях вызовы xStreamBufferSend() нужно сериализовать, при нескольких читателях — xStreamBufferReceive(); способ — вызов внутри критической секции с временем блокировки 0
  2. FreeRTOS. FreeRTOS-Kernel V11.3.3: include/stream_buffer.h (GitHub, MIT) — xStreamBufferCreate( xBufferSizeBytes, xTriggerLevelBytes ); xTriggerLevelBytes — число байтов, которое должно оказаться в буфере, чтобы задача, заблокированная на чтении, была разблокирована: при уровне 1 задача просыпается от одного байта, при уровне 10 — не раньше, чем накопится 10 байт, либо по истечении времени блокировки, и тогда получает сколько есть; уровень 0 заменяется на 1; уровень больше размера буфера недопустим; типы sbTYPE_STREAM_BUFFER, sbTYPE_MESSAGE_BUFFER, sbTYPE_STREAM_BATCHING_BUFFER; xStreamBufferSendFromISR() для записи из прерывания; xStreamBufferCreateStatic() с собственным массивом uint8_t
  3. FreeRTOS. FreeRTOS Kernel documentation: Direct to task notifications; FreeRTOS-Kernel V11.3.3 include/FreeRTOS.h — потоковые и message-буферы используют уведомление задачи с индексом 0; FreeRTOS.h: configUSE_STREAM_BUFFERS равно 1, если не определено
Страница помогла?

Нашли ошибку? ·