PBUF_POOL

В своих проектах мы используем вариант выделения памяти MEM_USE_POOLS . Причина в том ,чтобы уйти от "чудес" , связанных с динамическим выделением памяти тип malloc . То есть берем память под полный контроль . Сначала выделяем (резервируем) нужный объем памяти уже на этапе компиляции программы (смотрите массив memp_memory) и далее связанными блоками размещаем все что нам надо в этих регионах памяти , потом удаляем когда нужно. И всегда отслеживаем : сколько памяти осталось и т.д.

Память MEM_USE_POOLS на самом деле состоит из нескольких регионов [ RAW_PCB] , [UDP_PCB] , [TCP_PCB] и т.д.
Для каждого региона действует своя логика связывания блоков данных внутри данного региона. Но все блоки памяти в разных регионах объединяет одно - присутствие в начале каждого блока структуры memp, потом блок символов 0XCD 0XCD 0XCD 0XCD, потом данные (размер специфичен для каждого региона) и в конце у всех опять блок символов 0XCD 0XCD 0XCD 0XCD.

Блок 0XCD 0XCD 0XCD 0XCD0xCD устанавливается дефайнами MEMP_SANITY_REGION_BEFORE и MEMP_SANITY_REGION_AFTER . Их значения можно менять.

memp служит для быстрого нахождения первого свободного блока памяти для выделения внутри региона. Интересно что блоки памяти располагаются от конца к началу адресного пространства. В процессе работы свободные блоки перемешиваются и неважно где уже находится первый свободный блок (в конце , середине или начале), главное сколько их свободных осталось.

Чтобы понять как ведется контроль за размещением блоков данных в памяти надо понять как ведется связывание блоков памяти в пределах одного региона. Лучше рассмотреть на примере какого-нибудь региона. Нас волнует например PBUF_POOL . Сюда помещаются пакеты, приходящие из Ethernet канала для TCP соединений . Например цепочка сегментов post запроса, которые нам надо как-то собрать в одно целое и обработать.

Итак структура блока памяти региона PBUF_POOL :
struct memp
MEMP_SANITY_REGION_BEFORE (0XCD 0XCD 0XCD 0XCD)
struct pbuf
сами данные
MEMP_SANITY_REGION_AFTER (0XCD 0XCD 0XCD 0XCD)


struct pbuf

В структуре pbuf хранится указатель next на следующий блок памяти (сегмент tcp пакета). Таким образом происходит связывание пакетов (сегментов) в единое логическое целое.

В дополнение идет информация от общем количестве данных (в байтах ) через переменную tot_len. len это длина данных в конкретном блоке памяти.


struct pbuf {
  /** next pbuf in singly linked pbuf chain */
  struct pbuf *next;

  /** pointer to the actual data in the buffer */
  void *payload;

  /**
   * total length of this buffer and all next buffers in chain
   * belonging to the same packet.
   *
   * For non-queue packet chains this is the invariant:
   * p->tot_len == p->len + (p->next? p->next->tot_len: 0)
   */
  u16_t tot_len;

  /** length of this buffer */
  u16_t len;

  /** pbuf_type as u8_t instead of enum to save space */
  u8_t /*pbuf_type*/ type;

  /** misc flags */
  u8_t flags;

  /**
   * the reference count always equals the number of pointers
   * that refer to this pbuf. This can be pointers from an application,
   * the stack itself, or pbuf->next pointers from a chain.
   */

  u16_t ref;
};

Когда после принятия всех tcp сегментов от клиента у нас на сервере вызывается функция http_recv , то ей передается указатель на блок памяти (pbuf) в регионе PBUF_POOL.

Далее мы идем по цепочке next от блока к блоку , контролируя параметры len и tot_len у блоков.

Уничтожение , принятых tcp данных происходит сразу после http_parse_post в функции httpd_post_receive_data.


Можно добавить ,что в блоках памяти региона PBUF_POOL размещаются tcp сегменты без заголовков IP и Ethernet, то есть только содержание (http тело запроса).

В структуры memp и pbuf можно смело добавлять свои переменные для дополнительного контроля.