我目前正在使用带有 FreeRTOS 作为系统操作系统的 ARM Cortex M3 微控制器进行嵌入式项目。该代码是由一位前同事编写的,遗憾的是该项目有一些奇怪的错误,我必须尽快找到并修复它们。
简短描述:该设备集成到车辆中,并使用集成调制解调器将一些“特殊”数据发送到远程服务器。
主要问题:由于设备集成在车辆中,设备的电源随时可能丢失。因此,设备将“特殊”数据的某些部分存储到两个保留的闪存页中。该代码模块被布置为两个闪存页面上的 eeprom 仿真(用于磨损均衡和从一个闪存页面到另一个闪存页面的数据传输)。eeprom 仿真使用所谓的“虚拟地址”,您可以在其中将任何大小的数据块写入当前活动/有效的闪存页面,并使用这些虚拟地址将其读回。前同事将 eeprom 仿真实现为多任务模块,您可以在其中从应用程序中的每个任务读取/写入闪存页面。乍一看,一切似乎都很好。
但我的项目经理告诉我,设备总是会丢失一些“特殊”数据,此时车辆中的电源电压会下降到一些伏特,设备会尝试将数据保存到闪存中。通常电源电压约为 10-18 伏,但如果降至 7 伏以下,设备会收到一个称为中断的中断powerwarn
并触发一个称为 的任务powerfail task
。具有所有任务的powerfail task
最高优先级并执行一些回调,例如关闭调制解调器以及“特殊”数据存储在闪存页面中的位置。我试图理解代码并调试了几天/几周,现在我很确定我发现了问题:
在 powerfail 任务执行的那些回调(称为 powerfail 回调)中,有 RTOS 调用,其他任务在这些调用中被挂起。但不幸的是EEPROM_WriteBlock()
,在收到 powerwarn 中断之前,那些暂停的任务也可能有一个未完成的调用。因此,powerfail 任务执行回调,并且在其中一个回调中有一个EE_WriteBlock()
调用,其中任务无法获取互斥锁,EE_WriteBlock()
因为另一个任务(已暂停)已经获取了它--> 死锁!
这是将数据写入闪存的例程:
uint16_t
EE_WriteBlock (EE_TypeDef *EE, uint16_t VirtAddress, const void *Data, uint16_t Size)
{
.
.
xSemaphoreTakeRecursive(EE->rw_mutex, portMAX_DELAY);
/* Write the variable virtual address and value in the EEPROM */
.
.
.
xSemaphoreGiveRecursive(EE->rw_mutex);
return Status;
}
这是调用“xSemaphoreTakeRecursive()”时的 RTOS 特定代码:
portBASE_TYPE xQueueTakeMutexRecursive( xQueueHandle pxMutex, portTickType xBlockTime )
{
portBASE_TYPE xReturn;
/* Comments regarding mutual exclusion as per those within
xQueueGiveMutexRecursive(). */
traceTAKE_MUTEX_RECURSIVE( pxMutex );
if( pxMutex->pxMutexHolder == xTaskGetCurrentTaskHandle() )
{
( pxMutex->uxRecursiveCallCount )++;
xReturn = pdPASS;
}
else
{
xReturn = xQueueGenericReceive( pxMutex, NULL, xBlockTime, pdFALSE );
/* pdPASS will only be returned if we successfully obtained the mutex,
we may have blocked to reach here. */
if( xReturn == pdPASS )
{
( pxMutex->uxRecursiveCallCount )++;
}
else
{
traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex );
}
}
return xReturn;
}
我的项目经理很高兴我发现了这个错误,但他也强迫我尽快创建一个修复程序,但我真正想要的是重写代码。也许你们中的一个人可能会想,只要避免暂停其他任务就完成了,但这不是一个可能的解决方案,因为这可能会触发另一个错误。有没有人有一个快速的解决方案/想法我可以如何解决这个死锁问题?也许我可以使用xTaskGetCurrentTaskHandle()
inEE_WriteBlock()
来确定谁拥有互斥锁的所有权,然后在任务不再运行时给出它。
谢谢