Я работаю с ESP32-Wrover-DevKit с использованием Eclipse CDT 12.2019 , ESP-IDF framework и FreeRTOS .
Я использую одну очередь для сбора данных из нескольких задач (показания датчика). Один получатель очереди будет выводить данные через сокет TCP. Поскольку элемент очереди довольно большой, я решил поместить только указатель на элементы очереди, что должно быть в порядке согласно документации FreeRTOS , если память обрабатывается правильно.
Это является структурой данных, которую я использую для элементов очереди, обратите внимание на гибкий массив в конце структуры:
typedef struct mb32_packet_t {
uint16_t preamble;
uint8_t system_id;
uint8_t message_id;
uint8_t reserved;
uint16_t checksum;
uint32_t pay_len;
uint8_t payload[];
} __attribute__((packed)) mb32_packet_t;
Объявление и определение очереди:
#define MAX_QUEUE_SEND_ITEMS (25)
QueueHandle_t sys_link_send_queue;
sys_link_send_queue = xQueueCreate(MAX_QUEUE_SEND_ITEMS, sizeof(mb32_packet_t*));
Вот фрагмент одной из задач чтения датчика, которые помещают элементы в очередь:
mb32_packet_t *packet;
uint32_t pay_len = 8; // payload: 8 bytes
uint32_t pac_len = sizeof(*packet)+pay_len; // header: 11 bytes
packet = malloc(pac_len);
// ... code to assign header fields
// ... code to assign payload bytes
if(xQueueSend(sys_link_send_queue, &packet, portMAX_DELAY) != pdPASS) {
// release allocated memory in case the queue rejected the item
free(packet);
}
Вот фрагмент одного получателя:
void sys_link_task(void *pvParameters) {
while(1) {
mb32_packet_t* packet;
if(xQueueReceive(sys_link_send_queue, &packet, portMAX_DELAY) == pdPASS) {
// put packet bytes on the TCP stream (blocking mode)
tcp_server_send((uint8_t*)packet, packet->pay_len+11);
// finally release the packet memory
free(packet);
} else {
ESP_LOGE(TAG, "Failed to get message from queue.");
}
}
}
И, наконец, это реализация tcp_server_send()
function:
void tcp_server_send(uint8_t* buffer, size_t size) {
// send() can return less bytes than supplied length. Walk-around for robust implementation.
if(client_sock > 0) {
int to_write = size;
while(to_write > 0) {
int written = send(client_sock, buffer+(size-to_write), to_write, 0);
if(written < 0) {
printf("Failed to send data [w=%d]: %d", written, errno);
break;
}
to_write -= written;
}
}
}
Теперь только с одной сенсорной задачей все работает нормально. Как только я запускаю вторую задачу датчика, рано или поздно я получаю ошибки повреждения кучи. Иногда он работает нормально в течение нескольких секунд, иногда я сразу получаю эти ошибки.
Ошибка выглядит следующим образом:
CORRUPT HEAP: multi_heap.c:288 detected at 0x3ffc75e8
abort() was called at PC 0x4008da2e on core 1
ELF file SHA256: c4fc5b20ae785f9a890274f05fd4fcfcada76b29ea16a9f736ceabbea34086ad
Backtrace: 0x400913e9:0x3ffc95c0 0x40091785:0x3ffc95e0 0x4008da2e:0x3ffc9600 0x4008dda5:0x3ffc9620 0x4008413d:0x3ffc9640 0x4008416d:0x3ffc9660 0x40093a71:0x3ffc9680 0x40094557:0x3ffc96a0 0x400f4946:0x3ffc96c0 0x400f4987:0x3ffc96e0 0x400f4b0d:0x3ffc9700 0x400f4e8e:0x3ffc9720 0x400f4ee5:0x3ffc9770 0x400e2e43:0x3ffc97a0 0x400e2f52:0x3ffc97d0 0x400d3f89:0x3ffc97f0 0x4000bd83:0x3ffc9810 0x4000182a:0x3ffc9830 0x400d5e9c:0x3ffc9850 0x400d608c:0x3ffc9880 0x40093cd1:0x3ffc98b0
CPU halted.
Затем я запустил xtensa-esp32-elf-gdb
и посмотрел на символ в счетчик программ (P C):
PC 0x4008da2e -> split_if_necessary + 206 in section .iram0.text
Есть идеи, как решить эту проблему?
Мои мысли:
Я освобождаю память пакета слишком рано? Хотя сокет TCP находится в состоянии блокировки, как я понимаю (настройка по умолчанию). Однако, если сокет TCP не будет в состоянии блокировки, он, вероятно, также не будет работать при использовании одной задачи датчика. Поэтому я предполагаю, что я делаю что-то не так в отношении самой очереди или выделения / освобождения памяти.
Я также пытался использовать pvPortMalloc()
вместо malloc()
и vPortFree()
вместо free()
. Но без разницы, те же проблемы.