LightWeight IP: буфер не освобождается

Я использую стек TCP / IP под названием lwip. Я реализовал функцию ниже для отправки пакетов данных, вдохновленную аналогичной функцией обратного вызова, которая принимает пакеты данных.

Каждый раз при получении пакета я создаю буфер с помощью функции pbuf_alloc. Затем я отправляю пакет, используя udp_sendto. Наконец, я освобождаю буфер с помощью pbuf_free. (См. Код ниже.)

По какой-то причине pbuf_free не освобождает буфер. (Я получаю переполнение буфера после n пакетов, где n - размер пула.) lwip wiki предупреждает, что:

Сетевой драйвер также может не предполагать, что память pbuf действительно освобождена, когда он вызывает pbuf_free.

Как я могу заставить pbuf_free освободить буфер? Как избежать переполнения буфера?

(Моя реализация ниже.)

static err_t IAP_tftp_send_data_packet(struct udp_pcb *upcb, struct ip_addr *to, int to_port, int block)
{
  err_t err;
  struct pbuf *pkt_buf;
  char packet[TFTP_DATA_PKT_LEN_MAX];
  int bytesRead;
  int bytesToSend;

  /* Specify that we are sending data. */
  IAP_tftp_set_opcode(packet, TFTP_DATA); 

  /* Specify the block number that we are sending. */
  IAP_tftp_set_block(packet, block);

  bytesRead = IAP_tftp_set_data(packet, block);

  if(bytesRead != 0) {
    bytesToSend = TFTP_DATA_PKT_LEN_MAX - (512 - bytesRead + 1);
  } else {
    bytesToSend = TFTP_DATA_PKT_LEN_MAX - 512;
  }

  pkt_buf = pbuf_alloc(PBUF_TRANSPORT, bytesToSend, PBUF_POOL);

  if (!pkt_buf)
  {
    print("(TFTP) Buffer overflow!\r\n");
  }

  /* Copy the file data onto pkt_buf. */
  memcpy(pkt_buf->payload, packet, bytesToSend);

  err = udp_sendto(upcb, pkt_buf, to, to_port);

  /* free the buffer pbuf */
  printf("%d\n\r", pbuf_free(pkt_buf));

  return err;
}

person Randomblue    schedule 18.06.2012    source источник
comment
Вы проверяли счетчик ссылок на буфер? Боюсь, что он освобождается только в том случае, если счетчик ссылок равен 1.   -  person Fred    schedule 18.06.2012
comment
Возможно, udp_sendto берет ссылку и освобождает ее асинхронно (по таймеру?). Может, тебе просто нужно немного подождать?   -  person ugoren    schedule 18.06.2012
comment
@ugoren: Я пробовал ждать, пока происходит переполнение буфера, но переполнение буфера остается.   -  person Randomblue    schedule 18.06.2012
comment
Что печатает инструкция printf () при вызове pbuf_free ()?   -  person nos    schedule 20.07.2012


Ответы (4)


Какую версию lwIP вы используете? В зависимости от разных версий ответы сильно различаются.

Функция распределения memp_malloc (), вызванная внутри pbuf_alloc (), потерпела неудачу или цепочка pbufs не удалась, поэтому она возвращает NULL.

pbuf_alloc () также вернет NULL, если переданные аргументы также содержат NULL. (из-за проверки аргументов NULL).

В более новых версиях не могли бы вы показать, какое значение содержит макрос MEMP_OVERFLOW_CHECK? LwIP показывает другое поведение, когда значение макроса> = 2.

И еще одна причина может заключаться в том, что если вы используете многопоточность, механизмы блокировки внутри pbuf_alloc () не работают, что может привести к возврату NULL.

Некоторые версии требуют, чтобы вы вызывали pbuf_init () перед вызовом pbuf_alloc ().

Вы можете попробовать это:

pkt_buf = NULL;//Use NULL, just incase the NULL is not 0 as per your compiler.
pkt_buf = pbuf_alloc(PBUF_TRANSPORT, bytesToSend, PBUF_REF);
if(pkt_buf == NULL)
{
   printf("pbuf_alloc failed.\n");
}
else
{
   /* Do something with the allocated pbufs and free it. */
}

PBUF_REF не выделяет буферной памяти для pbuf. Pbuf следует использовать только в одном потоке, и если pbuf ставится в очередь, то для копирования буфера следует вызвать pbuf_take.

Вы также можете попробовать PBUF_RAM, который выделит буфер в ОЗУ.

Для получения дополнительной информации вы также можете просмотреть исходные файлы версии lwIP, которую вы используете.

person askmish    schedule 23.07.2012
comment
Сообщите мне, если этот ответ по-прежнему не отвечает на ваш вопрос. - person askmish; 17.08.2012
comment
Я назначил вам награду, потому что ваш ответ наиболее многообещающий. Возможно, мне придется спросить у вас более подробную информацию, поскольку у меня есть время для расследования. Спасибо. - person Randomblue; 22.08.2012
comment
Если вы чувствуете себя комфортно и у вас достаточно времени, я бы посоветовал вам просмотреть исходный код вашей версии, а не вики. Вики находится в плохом состоянии. - person askmish; 24.08.2012

Кажется, самым простым решением было бы создать буфер static, т.е. повторно использовать один и тот же буфер для каждого вызова:

static struct pbuf *pkt_buf = NULL;

if( pkt_buf == NULL )
    pkt_buf = pbuf_alloc(PBUF_TRANSPORT, bytesToSend, PBUF_POOL);
if( pkt_buf == NULL )
{
    print("(TFTP) Buffer overflow!\r\n");
}

Если ваш сценарий предполагает выгрузку / перезагрузку драйвера, произойдет утечка памяти. Чтобы исправить это, сделайте буфер статическим вне функции IAP_tftp_send_data_packet() и вызовите pbuf_free(), когда драйвер выгружается (при условии, что lwip сообщает вам).

person unwind    schedule 18.06.2012

Просто мимолетная мысль, возможно, совершенно бессмысленная. В этом коде:

if(bytesRead != 0) {
    bytesToSend = TFTP_DATA_PKT_LEN_MAX - (512 - bytesRead + 1);
} else {
    bytesToSend = TFTP_DATA_PKT_LEN_MAX - 512;
}
pkt_buf = pbuf_alloc(PBUF_TRANSPORT, bytesToSend, PBUF_POOL);

... может ли bytesRead принять значение 513 - TFTP_DATA_PKT_LEN_MAX?

Если бы это произошло, не завершился бы запрос на выделение нулевых байтов? (это можно проверить, напечатав значение bytesToSend при переполнении буфера и проверив, не равно ли оно нулю).

person LSerni    schedule 21.08.2012

struct pbuf не представляет собой непрерывную область памяти. Это скорее цепочка ячеек памяти. Таким образом, в общем случае это не сработает:

memcpy(pkt_buf->payload, packet, bytesToSend);

Вам нужно скопировать ваши данные. Функция memcpy () из фрагмента кода может переполнить буфер полезной нагрузки и вызвать всевозможные побочные эффекты, в том числе невозможность полностью освободить цепочку pbuf.

person Maxim Kharchenko    schedule 26.07.2013