语境

我正在使用FreeRTOS任务通知作为轻量级的事件组,如FreeRTOS文档here中所述。这个想法是使两个简单的任务以交替的顺序打印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 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/

10-13 09:46