加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

写入flash页面时powerfail序列的死锁

发布时间:2020-12-15 07:25:55 所属栏目:百科 来源:网络整理
导读:我目前正在开发一个使用ARM Cortex M3微控制器和FreeRTOS作为系统操作系统的嵌入式项目.代码是由一位前同事编写的,遗憾的是该项目有一些奇怪的错误,我必须尽快找到并修复. 简短说明:该设备集成到车辆中,并使用集成调制解调器向远程服务器发送一些“特殊”数
我目前正在开发一个使用ARM Cortex M3微控制器和FreeRTOS作为系统操作系统的嵌入式项目.代码是由一位前同事编写的,遗憾的是该项目有一些奇怪的错误,我必须尽快找到并修复.

简短说明:该设备集成到车辆中,并使用集成调制解调器向远程服务器发送一些“特殊”数据.

主要问题:由于设备集成在车辆中,设备的电源可能随时丢失.因此,设备将“特殊”数据的某些部分存储到两个保留的闪存页面.此代码模块在两个闪存页面上作为eeprom仿真进行布局(用于从一个闪存页面到另一个闪存页面的耗损均衡和数据传输).
eeprom仿真与所谓的“虚拟地址”一起使用,您可以在其中将任意大小的数据块写入当前活动/有效的闪存页面,并使用这些虚拟地址将其读回.
前同事将eeprom仿真实现为多任务模块,您可以从应用程序中的每个任务读取/写入闪存页面.乍一看,一切似乎都很好.

但是我的项目经理告诉我,设备总是会丢失一些“特殊”数据,车辆中的电源电压下降到几伏,设备会尝试将数据保存到闪存中.
通常电源大约为10-18伏,但如果电压降至7伏以下,则设备会收到一个名为powerwarn的中断,并触发一个名为powerfail任务的任务.
powerfail任务具有所有任务的最高优先级,并执行一些回调,例如,调制解调器关闭,“特殊”数据也存储在闪存页面中.
我试着理解代码并调试了几天/几周,现在我很确定我发现了问题:

在powerfail任务执行的那些回调中(称为powerfail回调),有RTOS调用,
其他任务被暂停的地方.但不幸的是,那些被填充的任务也可能在接收到电源警告中断之前有一个未完成的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;
}

我的项目经理很高兴我发现了这个bug,但他也强迫我尽快创建一个修复程序,但我真正想要的是重写代码.
也许你们中的一个人可能会想,只是避免暂停其他任务而你们已经完成了,但这不是一个可能的解决方案,因为这可能会引发另一个错误.
有没有人能快速解决/想法如何解决这个死锁问题?
也许我可以在EE_WriteBlock()中使用xTaskGetCurrentTaskHandle()来确定谁拥有互斥锁的所有权,然后在任务不再运行时给它.

谢谢

解决方法

在许多系统上写入闪存需要在写入期间禁用中断,因此我不确定在写入过程中如何使powerFail运行,但无论如何:

不要使用互斥锁直接控制对保留的Flash页面的访问 – 请使用阻塞生成者 – 使用者队列.

通过将请求排队到一个’flashWriter’线程,将所有这些写入委托给它.如果请求写入的线程需要同步访问,请在请求结构中包含请求线程在推送其请求后等待的事件或信号量. flashWriter完成后可以发出信号(或者在加载带有错误指示的结构:)之后发出信号.

主题有变化 – 如果所有写请求线程只需要同步访问,也许他们可以使用自己的信号量保留自己的静态请求结构,并排队指向它的指针.

使用允许在队列头部进行高优先级推送的生产者 – 消费者队列类,并且当powerfail运行时,在队列的前面按下“stopWriting”请求.然后flashWriter将完成正在进行的任何写入操作,弹出stopWriting请求,因此被指示暂停自身,(或者您可以使用flashWriter每次尝试弹出队列之前检查的’stop’易失性布尔值).

这应该通过从其他线程中推送的闪存写入请求中删除硬互斥锁来防止死锁.其他线程是否继续排队写入请求并不重要 – 它们永远不会被执行.

编辑:我刚吃了两杯咖啡,考虑到这一点,’flashWriter’线程很容易成为’FlashWriterAndPowerFail’线程:

如果设置了volatile’stop’布尔值,则可以安排生产者 – 消费者队列返回null的pop()结果,无论队列中是否有条目或没有.在’FWAPF’线程中,在每次pop()返回后进行空检查,并在null时执行powerFail操作,否则执行flashWrite操作.

当powerFail中断发生时,设置stop bool并发出队列中’count’信号量的信号,以确保FWAPF线程在队列中当前被阻塞时运行.

这样,你不需要一个单独的’powerFail’线程和堆栈 – 一个线程可以执行flashWrite和powerFail,同时仍然确保没有互斥锁死锁.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读