В своих проектах мы используем вариант выделения памяти 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 можно смело добавлять свои переменные для дополнительного контроля.