Как лучше всего обрабатывать большие буферы в многоуровневом стеке протоколов?

Я работаю над простым стеком протоколов для небольшой встраиваемой системы (многоточечный, тип rs485). В этом стеке бесполезные модели после уровней OSI:

  1. Применение
  2. Сеть
  3. Канал передачи данных
  4. физический (последовательный драйвер)

У каждого уровня есть своя собственная часть верхнего / нижнего колонтитула, которая обертывает полезную нагрузку слоя над ним.

Я буду использовать свой собственный буферный пул статически распределенных блоков фиксированного размера для хранения двоичных пакетов. (В этом приложении нет malloc / free.)

В других API я видел, что данные обычно передаются как константный указатель с соответствующей длиной. Таким образом, для данных потребуется операция копирования на каждом уровне, поскольку полезная нагрузка уровня выше помещается во вновь выделенный буфер для текущего уровня.

Для трехуровневого стека это будет 2 операции копирования и 3 выделенных буфера.

Есть ли лучший способ сделать это, сохранив при этом чистое разделение уровней протокола?

Чтобы лучше закрепить обсуждение, допустим, что пакеты обычно имеют размер около 2 КБ, а процессор представляет собой небольшой 8-битный микроконтроллер, работающий на частоте 8 МГц.


person JeffV    schedule 31.08.2009    source источник
comment
8-битный микро @ 8Mhz и пакет 2kB? Вы не упомянули о доступной оперативной памяти, но я предполагаю, что пакет в значительной степени заполняет ее, и вы явно запускаете один процесс, я думаю, я не вижу причины для создания такого количества слоев и абстракций для такой простой системы. разбейте его на передачу данных и приложение и передайте полезную нагрузку через указатель на глобальный.   -  person Mark    schedule 31.08.2009
comment
@Mark, стек протоколов должен использоваться на разных платформах, одна из них - Atmega1281 с оперативной памятью 8k. Он может работать на частоте 20 МГц, но мы не делаем этого из соображений мощности. Я мог бы ослабить разделение проблем, но мой вопрос не в этом.   -  person JeffV    schedule 01.09.2009
comment
@Mark, вы правы насчет буфера 2k, я, скорее всего, не смогу увеличить его, но для этого приложения чем больше, тем лучше, так как это канал данных с высокой задержкой (спутник), и я не планирую добавлять оконное управление (как это сделано с TCP).   -  person JeffV    schedule 01.09.2009


Ответы (3)


Вы можете избежать копий, если каждый уровень будет запрашивать пустой буфер у следующего нижнего уровня, а не выделять его самому:

  • Уровень приложений запрашивает длину буфера LA у сетевого уровня.
  • Сетевой уровень запрашивает длину буфера LA + LN от уровня канала передачи данных.
  • Уровень Datalink запрашивает длину буфера LA + LN + LD у физического уровня.
  • Физический уровень извлекает буфер из пула буферов.
  • Физический уровень возвращает buffer + phdr_len на уровень Datalink.
  • Уровень канала передачи данных возвращает buffer + phdr_len + dhdr_len на сетевой уровень.
  • Сетевой уровень возвращает buffer + phdr_len + dhdr_len + nhdr_len на уровень приложения.
  • Уровень приложения заполняет данные в предоставленном буфере и вызывает сетевой уровень для передачи.
  • Сетевой уровень добавляет заголовок и вызывает Datalink Layer для передачи.
  • Уровень канала передачи данных добавляет заголовок и вызывает физический уровень для передачи.
  • Физический уровень добавляет заголовок в начало и передает аппаратному обеспечению.
person caf    schedule 31.08.2009
comment
Или заголовки для разных слоев не обязательно должны быть смежными (в одном буфере): вместо этого они могут находиться в разных буферах с помощью API-интерфейса сбора разброса. - person ChrisW; 31.08.2009
comment
Спасибо @caf, я тоже думал об этом как о возможном решении. В момент вызова get_buffer () верхний уровень возьмет на себя ответственность за буфер. В дополнение к send (pbuf) на каждом уровне потребуется free_buffer () на случай, если что-то пойдет не так, чтобы буфер мог быть освобожден должным образом. - person JeffV; 31.08.2009

Создайте буферную структуру. Зная максимальный размер нижнего уровня, выделите достаточно буферного пространства на верхнем уровне, чтобы добавлять каждый последующий слой вниз по стеку. Каждый слой перемещает указатель в структуре буфера по мере добавления слоя.

На нижнем уровне начало буфера записывается в указатель в структуре буфера. Отправляемые данные находятся в непрерывном буфере. На каждом уровне данные не копировались.

Двигаясь снизу вверх, вы снимаете слои внутри буферной структуры.

person DanM    schedule 31.08.2009
comment
Это самый простой подход, так как он требует только одного буфера и не требует копирования. Для ведомого устройства буфер обычно определяется на физическом уровне, поскольку именно отсюда отправляются сообщения. - person Steve Melnikoff; 31.08.2009
comment
А если у вас разные типы пакетов, вы просто ссылаетесь на указатель структуры другого типа. - person Luka Rahne; 31.08.2009
comment
Проблема заключается в том, что верхний уровень должен знать, сколько нужно каждому нижнему уровню, что является жесткой связью, которую OP пытается избежать (я считаю, что это была ссылка на чистое разделение уровней протокола). - person caf; 01.09.2009
comment
@caf: действительно. Для ведомых устройств обычно это нижний слой, который содержит буфер. Это позволяет избежать проблемы, поскольку максимальный размер сообщения часто является частью спецификации нижнего уровня. Если верхний уровень должен содержать буфер, эта информация должна быть известна верхнему уровню. Я полагаю, что это меньшее из двух зол, если ресурсы ограничены! - person Steve Melnikoff; 03.09.2009
comment
Успех этого подхода часто зависит от того, насколько вы контролируете стек протоколов и драйверы устройств. Я работал над некоторыми встраиваемыми системами с низким энергопотреблением, где у меня был полный контроль над стеком протоколов и драйверами - избегание копирования между потоками / слоями позволило нам повысить пропускную способность. - person DanM; 03.09.2009

В других API я видел, что данные обычно передаются как константный указатель с соответствующей длиной. Таким образом, для данных потребуется операция копирования на каждом уровне, поскольку полезная нагрузка уровня выше помещается во вновь выделенный буфер для текущего уровня.

Я предполагаю, что вы приводите пример API для буфера передачи.

Я думаю, что вы можете сохранить тот же API, если вы добавите ограничение, что любому, кто вызывает этот API, не разрешается использовать или касаться этого буфера снова, пока они не получат последующее уведомление о завершении операции передачи: так что вызов API неявно передача права собственности на этот буфер.

person ChrisW    schedule 31.08.2009