我是FreeRTOS的新手,所以我从我认为是一个很棒的教程开始,这个教程是由肖恩·海梅尔提供的。我还在实现在ESP32 DevkitC V4中编写的代码。
但是,我认为我不理解二进制信号量和互斥量之间的区别。当我运行这段代码时,试图避免使用两个互斥保护关键部分(如本教程所示)的两个任务之间的死锁。
// Use only core 1 for demo purposes
#if CONFIG_FREERTOS_UNICORE
static const BaseType_t app_cpu = 0;
#else
static const BaseType_t app_cpu = 1;
#endif
//Settings
TickType_t mutex_timeout = 1000 / portTICK_PERIOD_MS;
//Timeout for any task that tries to take a mutex!
//Globals
static SemaphoreHandle_t mutex_1;
static SemaphoreHandle_t mutex_2;
//**********************************************************
//Tasks
//Task A (High priority)
void doTaskA(void*parameters){
while(1){
//Take mutex 1
if( xSemaphoreTake(mutex_1, mutex_timeout) == pdTRUE){
Serial.println("Task A took mutex 1");
vTaskDelay(1 / portTICK_PERIOD_MS);
//Take mutex 2
if(xSemaphoreTake(mutex_2, mutex_timeout) == pdTRUE){
Serial.println("Task A took mutex 2");
//Critical section protected by 2 mutexes
Serial.println("Task A doing work");
vTaskDelay(500/portTICK_PERIOD_MS); //simulate that critical section takes 500ms
} else {
Serial.println("Task A timed out waiting for mutex 2. Trying again...");
}
} else {
Serial.println("Task A timed out waiting for mutex 1. Trying again...");
}
//Return mutexes
xSemaphoreGive(mutex_2);
xSemaphoreGive(mutex_1);
Serial.println("Task A going to sleep");
vTaskDelay(500/portTICK_PERIOD_MS);
//Wait to let other task execute
}
}
//Task B (low priority)
void doTaskB(void * parameters){
while(1){
//Take mutex 2 and wait to force deadlock
if(xSemaphoreTake(mutex_2, mutex_timeout)==pdTRUE){
Serial.println("Task B took mutex 2");
vTaskDelay(1 / portTICK_PERIOD_MS);
if(xSemaphoreTake(mutex_1, mutex_timeout) == pdTRUE){
Serial.println("Task B took mutex 1");
//Critical section protected by 2 mutexes
Serial.println("Task B doing work");
vTaskDelay(500/portTICK_PERIOD_MS); //simulate that critical section takes 500ms
} else {
Serial.println("Task B timed out waiting for mutex 1");
}
} else {
Serial.println("Task B timed out waiting for mutex 2");
}
//Return mutexes
xSemaphoreGive(mutex_1);
xSemaphoreGive(mutex_2);
Serial.println("Task B going to sleep");
vTaskDelay(500/portTICK_PERIOD_MS);
//Wait to let other task execute
}
}
void setup(){
Serial.begin(115200);
vTaskDelay(1000 / portTICK_PERIOD_MS);
Serial.println();
Serial.println("---FreeRTOS Deadlock Demo---");
//create mutexes
mutex_1 = xSemaphoreCreateMutex();
mutex_2 = xSemaphoreCreateMutex();
//Start task A (high priority)
xTaskCreatePinnedToCore(doTaskA, "Task A", 1500, NULL, 2, NULL, app_cpu);
//Start task B (low priority)
xTaskCreatePinnedToCore(doTaskB, "Task B", 1500, NULL, 1, NULL, app_cpu);
vTaskDelete(NULL);
}
void loop(){
}
在两个任务执行过程中到达第一个互斥体后,我的ESP32将自动重新启动,并显示以下消息:
---FreeRTOS Deadlock Demo---
Task A took mutex 1
Task B took mutex 2
Task A timed out waiting for mutex 2. Trying again...
assert failed: xQueueGenericSend queue.c:832 (pxQueue->pcHead != ((void *)0) || pxQueue->u.xSemaphore.xMutexHolder == ((void *)0) || pxQueue->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle())
我无法解释这个错误。但是,当我将互斥的定义更改为安装程序()中的二进制信号量时:
//create mutexes
mutex_1 = xSemaphoreCreateBinary();
mutex_2 = xSemaphoreCreateBinary();
代码在ESP32中运行良好。有人能解释一下为什么会这样吗?非常感谢和抱歉,如果问题没有充分提出,因为这是我的第一个问题。
发布于 2022-11-04 18:24:45
信号量和互斥量之间的关键区别之一是所有权的概念。信号量,没有一条线拥有它们。即使优先级较低的线程已经获得信号量,高优先级线程也可以获得信号量。另一方面,互斥是由获取它们的线程拥有的,并且只能由该线程释放。
在上面的代码中,mutex_1是由Task获得的,mutex_2是由Task获得的。此时,Task试图获取mutex_2。当它是一个实际的互斥体时,Task无法获得它,因为它属于Task。但是,如果这是一个信号量,任务A可以从Task获得它,从而清除死锁。
这里的错误也与此有关。在任务A超时等待mutex_2之后,它开始释放互斥对象。它可以发布mutex_1没有问题,因为它拥有它。当它试图释放mutex_2时,它不能释放,因为它不是所有者。因此,操作系统抛出一个错误,因为一个任务不应该试图释放它不拥有的互斥对象。
如果您想更多地了解互斥和信号量之间的差异,可以查看这个文章。
https://stackoverflow.com/questions/73791395
复制相似问题