【发布时间】:2014-06-09 12:23:01
【问题描述】:
我目前正在使用带有 FreeRTOS 作为系统操作系统的 ARM Cortex M3 微控制器进行嵌入式项目。该代码是由一位前同事编写的,遗憾的是该项目有一些奇怪的错误,我必须尽快找到并修复。
简短描述:该设备集成到车辆中,并使用集成调制解调器将一些“特殊”数据发送到远程服务器。
主要问题:由于设备集成在车辆中,设备的电源随时可能丢失。因此,设备将“特殊”数据的某些部分存储到两个保留的闪存页中。该代码模块被布置为两个闪存页面上的 eeprom 仿真(用于磨损均衡和从一个闪存页面到另一个闪存页面的数据传输)。 eeprom 仿真与所谓的“虚拟地址”一起工作,您可以在其中将任何大小的数据块写入当前活动/有效的闪存页面,并使用这些虚拟地址将其读回。 前同事将 eeprom 仿真实现为多任务模块,您可以在其中从应用程序中的每个任务读取/写入闪存页面。乍一看,一切似乎都很好。
但我的项目经理告诉我,设备总是会在某些时候丢失一些“特殊”数据,此时车辆的电源电压会下降到一些伏特,设备会尝试将数据保存到闪存中。
通常电源电压约为 10-18 伏,但如果降至 7 伏以下,设备会收到名为powerwarn 的中断,并触发名为powerfail task 的任务。
powerfail task 在所有任务中具有最高优先级,并执行一些回调,例如调制解调器关闭,并且“特殊”数据存储在闪存页面中。
我试图理解代码并调试了几天/几周,现在我很确定我找到了问题:
在 powerfail 任务执行的回调(称为 powerfail 回调)中,有 RTOS 调用,
其他任务被暂停的地方。但不幸的是,在收到 powerwarn 中断之前,那些暂停的任务也可能有一个未完成的EEPROM_WriteBlock() 调用。
因此,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;
}
我的项目经理很高兴我发现了这个错误,但他也强迫我尽快修复,但我真正想要的是重写代码。
也许你们中的一个人可能会想,只要避免暂停其他任务就可以了,但这不是一个可能的解决方案,因为这可能会触发另一个错误。
有没有人有一个快速的解决方案/想法我可以如何解决这个死锁问题?
也许我可以在EE_WriteBlock() 中使用xTaskGetCurrentTaskHandle() 来确定谁拥有互斥锁的所有权,然后在任务不再运行时提供它。
谢谢
【问题讨论】: