LWIP зависает через некоторое время из-за того, что память не освобождается

#c #stm32 #freertos #lwip

#c #stm32 #freertos #lwip

Вопрос:

OBS: Я использую микроконтроллер STM32F2, FreeRTOS и LwIP, и я использую raw API

У меня есть приложение, в котором я прослушиваю один компьютер и подключаюсь к другому. В принципе, некоторое время все работает нормально, когда я пытаюсь достичь высокой пропускной способности, но примерно через полчаса… около 80 ~ 90 тыс. полученных пакетов зависает. На самом деле это немного меняется там, где оно зависает, но оно перестало это делать, когда я начал закрывать соединение всякий раз, когда tcp_write возвращает ошибку err_mem.

Иногда он зависает в этой строке:

 /* useg should point to last segment on unacked queue */

  useg = pcb->unacked;

  if (useg != NULL) {

    for (; useg->next != NULL; useg = useg->next);  <------- here

  }
  

Иногда, когда я вызываю tcp_write, он возвращает ERR_MEM и он никогда не возвращает ничего, кроме after ERR_MEM. Вот как я отправляю данные, в основном я принимаю соединение, получаю данные, сохраняю печатную плату, что-то делаю, а затем отвечаю на ту же печатную плату:

 err_t ret;
ret = tcp_write(g_response[i].pcb, data, len, 1);
if(ret == ERR_OK)
    tcp_output(g_response[i].pcb);
else
    tcp_close(g_response[i].pcb);
  

Вот как я настраиваю сокет для прослушивания:

 pcb = tcp_new();
tcp_bind(pcb, IP_ADDR_ANY, port);
pcb = tcp_listen(pcb);
pcb->so_options |= SOF_KEEPALIVE; // enable keep-alive
pcb->keep_intvl = 1000; // sends keep-alive every second
tcp_accept(pcb, accept);
  

И вот мои обратные вызовы для sent и rcv

 static err_t rcv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) {
    if(p == NULL) {
        return ERR_OK;
    } else if(err != ERR_OK) {
        return err;
    }

    tcp_recved(pcb, p->len);
    // do something

    pbuf_free(p);

    return ERR_OK;
}

int sentcounter = 0;
static err_t sent(void *arg, struct tcp_pcb *pcb, uint16_t len) {

    sentcounter  ;
    return ERR_OK;
}

static err_t accept(void *arg, struct tcp_pcb *pcb, err_t err) {
    int i;
    tcp_arg(pcb, NULL);
    /* Set up the various callback functions */
    tcp_recv(pcb, rcv);
    tcp_err(pcb, error);
    tcp_sent(pcb, sent);

    tcp_accepted(pcb);
}
  

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

Мне действительно нужна высокая пропускная способность, вот почему я вызываю tcp_output вместо того, чтобы позволить tcpip_thread обрабатывать отправку пакета. Всякий раз, когда я позволяю этому потоку извлекать часть пакета, все просто работает, но это слишком медленно (возможно, по одному пакету каждые 200 ~ 300 мс, и, вызывая tcp_output в функции, я дошел до того, что отправляю данные на 10 мс меньше… Я также не передаю большие объемы данных, так что это помогает).

Недавно я заметил, что tcpip_thread вызывает функцию ввода, переходит к ipv4_input, переходит к memp_free и продолжает работу, но никогда не завершается (на самом деле я запускаю новый тест прямо сейчас, поэтому позже я обновлю этот вопрос с помощью callstack для ввода, прежде чем он зависнет).

Кто-нибудь делал что-нибудь подобное? Пакеты небольших размеров с высокой пропускной способностью?

РЕДАКТИРОВАТЬ: Как и было обещано, вот мой стек вызовов

osMutexWait() в cmsis_os.c:681 0x800474c sys_arch_protect() в

sys_arch.c: 400 0x80146a6 do_memp_free_pool() в memp.c: 415 0x800dca2

memp_free() в memp.c: 486 0x800dcf8 tcp_seg_free() в tcp.c: 1.336

0x800fb0e tcp_receive() в tcp_in.c:1.162 0x8011712 tcp_process() в

tcp_in.c: 877 0x8011048 tcp_input() в tcp_in.c: 367 0x8010692

ip4_input() на ip4.c: 670 0x800c688 ethernet_input() на ethernet.c: 176

0x80142fe tcpip_thread() в tcpip.c: 124 0x8006836

pxPortInitialiseStack() на порту.c:231 0x8004cd0

Как и сказал @Lundin, это проблема параллелизма. Вероятно, мне следует попытаться быть более осторожным с тем, как вызываются функции. Я попытаюсь изменить свой код для работы с netconn или socket вместо tcp_pcb, а затем измерю скорость, которую я получу. Мне действительно нужна высокая пропускная способность

Комментарии:

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

2. В любом случае, эта ошибка пахнет условиями гонки. Вы должны начать с проверки того, как переменные и разделяются между потоками / ISR. Есть ли общие файлы, которые не защищены? Вы вообще не публиковали никакого кода, содержащего мьютекс или подобное. Являются ли функции tcp потокобезопасными?

3. @Lundin Я знаю… но я так долго занимался этим кодом, что даже не заметил, что на самом деле использую pcb… в моей голове это был просто tcp_pcb. О вашем предложении… мой стек вызовов, похоже, вас устраивает. Есть идеи? Я все еще тестирую несколько исправлений и после этого переписываю свой код

4. Нет, у меня нет никаких идей, поскольку вы не опубликовали никакого кода, содержащего защиту от состояния гонки. Если такого кода нет, что ж, неудивительно…