语境
我正在使用FreeRTOS任务通知作为轻量级的事件组,如FreeRTOS文档here中所述。这个想法是使两个简单的任务以交替的顺序打印Ping
和Pong
,每一个完成的打印时互相通知。
问题
该程序成功编译,但是在启动后由于以下堆栈跟踪而崩溃:
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 flash\n", 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);
}
最佳答案
实际上,这里有2个错误,并且错误消息并不能说明任何一个。
问题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) {
此时,您的代码应按预期运行。
关于c - FreeRTOS xTaskNotifyWait导致某些内部互斥断言失败,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57795556/