c-FreeRTOS xTaskNotifyWait导致某些内部互斥断言失败



上下文

我使用FreeRTOS任务通知作为轻量级事件组,如FreeRTOS文档中所述。这个想法是让两个简单的任务以交替的顺序打印PingPong,每个任务在打印完成时相互通知。

问题

该程序成功编译,但在启动后立即崩溃,并带有以下堆栈跟踪:

assertion "coreID == mux->owner" failed: file "/home/micrified/Documents/Somnox/esp-idf/components/freertos/portmux_impl.inc.h", line 157, function: vPortCPUReleaseMutexIntsDisabledInternal
abort() was called at PC 0x400d58cf on core 1
0x400d58cf: __assert_func at /builds/idf/crosstool-NG/.build/xtensa-esp32-elf/src/newlib/newlib/libc/stdlib/assert.c:62 (discriminator 8)

ELF file SHA256: bd56229aae2c8bfb3479e7a47ebb97d9be7e36483cea95737781c99e1daa0200
Backtrace: 0x400849cd:0x3ffb69a0 0x40084d41:0x3ffb69c0 0x400d58cf:0x3ffb69e0 0x400883ab:0x3ffb6a10 0x40087a3a:0x3ffb6a30 0x40087af9:0x3ffb6a70 0x40082755:0x3ffb6a90 0x40082841:0x3ffb6ac0 0x400d3697:0x3ffb6ae0 0x400d2609:0x3ffb6b00 0x4000bd83:0x3ffb6b20 |<-CORRUPTED
0x400849cd: invoke_abort at /home/micrified/Documents/Somnox/esp-idf/components/esp32/panic.c:155
0x40084d41: abort at /home/micrified/Documents/Somnox/esp-idf/components/esp32/panic.c:172
0x400d58cf: __assert_func at /builds/idf/crosstool-NG/.build/xtensa-esp32-elf/src/newlib/newlib/libc/stdlib/assert.c:62 (discriminator 8)
0x400883ab: vPortCPUReleaseMutexIntsDisabledInternal at /home/micrified/Documents/Somnox/esp-idf/components/freertos/portmux_impl.inc.h:157
(inlined by) vPortCPUReleaseMutexIntsDisabled at /home/micrified/Documents/Somnox/esp-idf/components/freertos/portmux_impl.h:110
(inlined by) vTaskExitCritical at /home/micrified/Documents/Somnox/esp-idf/components/freertos/tasks.c:4261
0x40087a3a: xQueueGenericReceive at /home/micrified/Documents/Somnox/esp-idf/components/freertos/queue.c:1542
0x40087af9: xQueueTakeMutexRecursive at /home/micrified/Documents/Somnox/esp-idf/components/freertos/queue.c:635
0x40082755: lock_acquire_generic at /home/micrified/Documents/Somnox/esp-idf/components/newlib/locks.c:157
0x40082841: _lock_acquire_recursive at /home/micrified/Documents/Somnox/esp-idf/components/newlib/locks.c:171
0x400d3697: uart_write at /home/micrified/Documents/Somnox/esp-idf/components/vfs/vfs_uart.c:194
0x400d2609: esp_vfs_write at /home/micrified/Documents/Somnox/esp-idf/components/vfs/vfs.c:420 (discriminator 4)

我自己没有直接与任何FreeRTOS信号量或互斥对象类型接口,所以我不确定这个错误的原因是什么

可复制示例

我的程序代码相当简单。我正在ESP-32开发板上运行它,并使用ESP-IDF工具套件进行构建。


#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"

#define A_BIT   0x1
#define B_BIT   0x2
TaskHandle_t handle_task_a;
TaskHandle_t handle_task_b;
void a_task (void *pvParameters) {
BaseType_t xResult;
uint32_t value;
while (1) {
// Block waiting for notification
xResult = xTaskNotifyWait(pdFALSE, // Don't clear bits on entry
A_BIT, // Clear own bit on exit
&value, // Save value 
portMAX_DELAY); // Block indefinitely
if (xResult != pdPASS) {
fprintf(stderr, "Err: Task A bad notify!n");
return;
}
// Ignore if bit not set
if ((value & A_BIT) == 0) {
continue;
}
// Otherwise wait a second
vTaskDelay(1000 / portTICK_PERIOD_MS);
// Show message
printf("Task A: Ping!n");
// Dispatch notification to task B
xTaskNotify(handle_task_b, B_BIT, eSetBits);
}
}
void b_task (void *pvParameters) {
BaseType_t xResult;
uint32_t value;
while (1) {
// Block waiting for notification
xResult = xTaskNotifyWait(pdFALSE, // Don't clear bits on entry
B_BIT, // Clear own bit on exit
&value, // Save value 
portMAX_DELAY); // Block indefinitely
if (xResult != pdPASS) {
fprintf(stderr, "Err: Task B bad notify!n");
return;
}
// Ignore if bit not set
if ((value & B_BIT) == 0) {
continue;
}
// Otherwise wait a second
vTaskDelay(1000 / portTICK_PERIOD_MS);
// Show message
printf("Task B: Pong!n");
// Dispatch notification to task A
xTaskNotify(handle_task_a, A_BIT, eSetBits);
}
}

void app_main(void)
{
/* Print chip information */
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
printf("This is ESP32 chip with %d CPU cores, WiFi%s%s, ",
chip_info.cores,
(chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
(chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "");
printf("silicon revision %d, ", chip_info.revision);
printf("%dMB %s flashn", spi_flash_get_chip_size() / (1024 * 1024),
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");
// Create task A 
if(xTaskCreate(a_task, "Task A", 512, NULL, tskIDLE_PRIORITY, &handle_task_a) != pdPASS) {
fprintf(stderr, "Error creating task A!n");
return;
}
// Create task B
if (xTaskCreate(b_task, "Task B", 512, NULL, tskIDLE_PRIORITY, &handle_task_b) != pdPASS) {
fprintf(stderr, "Error creating task B!n");
return;
}
// Kick off by notifying task A
xTaskNotify(handle_task_a, A_BIT, eSetBits);
}

这里实际上有两个错误,错误消息并没有告诉我们任何一个错误。

问题1:调度器

在app_main()中,您可以创建任务,但从不启动调度程序。如果调度程序没有运行,它将永远不会启动任务。你需要像这样的东西

//ERROR: you cant call this before the scheduler starts
// Kick off by notifying task A
//xTaskNotify(handle_task_a, A_BIT, eSetBits);
// Start the real time scheduler.
vTaskStartScheduler();
// Will not get here unless there is insufficient RAM.
}

https://www.freertos.org/a00132.html

这只是问题的一部分,即使这样,您的代码也不会运行。还有另一个错误:在启动调度程序之前,您不能调用xTaskNotify,这将导致我们进入下一点。。。

问题2:死锁

由于我们无法在运行调度程序之前调用xTaskNotify,因此不会发生任何事情。编写任务的方式会造成死锁。

  • 任务A等待来自B的通知
  • 任务B等待来自A的通知

所以问题是将xTaskNotify放在哪里以防止死锁?

有一些解决方案,您可以使用按钮或计时器中断来触发第一个xTaskNotify(如果您从ISR执行此操作,请确保使用xTaskNotifyFromISR),并使整个系统运行。更简单的方法是将其添加到某个任务的启动代码中。

void b_task (void *pvParameters) {
BaseType_t xResult;
uint32_t value;
//First we wait a little for the scheduler to settle
vTaskDelay(1000 / portTICK_PERIOD_MS);
// Kick off by notifying task A
xTaskNotify(handle_task_a, A_BIT, eSetBits);
while (1) {

此时,您的代码应该按预期运行。

相关内容

最新更新