[英]Deadlock of powerfail sequence during write to flash page
我目前正在使用將FreeRTOS作為系統OS的ARM Cortex M3微控制器進行嵌入式項目。 該代碼是由一位前同事編寫的,可惜該項目存在一些怪異的錯誤,我必須盡快找到並修復。
簡短說明:該設備已集成到車輛中,並使用集成調制解調器將一些“特殊”數據發送到遠程服務器。
主要問題:由於該設備已集成到車輛中,因此該設備的電源可能隨時丟失。 因此,設備將“特殊”數據的某些部分存儲到兩個保留的閃存頁中。 該代碼模塊作為eeprom仿真在兩個閃存頁面上進行布局(用於損耗均衡和從一個閃存頁面到另一閃存頁面的數據傳輸)。 eeprom仿真使用所謂的“虛擬地址”,您可以在其中將任意大小的數據塊寫入當前活動/有效的閃存頁面,並使用這些虛擬地址將其讀回。 這位前同事將eeprom仿真實現為多任務模塊,您可以在其中從應用程序中的每個任務讀取/寫入Flash頁面。 乍看起來,一切似乎都很好。
但是我的項目經理告訴我,設備有時會丟失一些“特殊”數據,此時車輛的電源電壓下降到幾伏特,設備會嘗試將數據保存到閃存中。 通常,電源約為10-18伏,但是如果電源降至7伏以下,則設備會收到一個稱為powerwarn
的中斷,並觸發一個稱為powerfail task
。 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()
來確定誰擁有該互斥鎖的所有權,然后在任務不再運行時給出它。
謝謝
在許多系統上,寫入閃存需要在寫入期間禁用中斷,因此我不確定在寫入過程中如何使powerFail運行,但是無論如何:
不要使用互斥鎖直接控制對保留的Flash頁的訪問-而是使用阻止生產者-消費者隊列。
通過對請求進行排隊,將所有這些寫入委托給一個“ flashWriter”線程。 如果請求寫操作的線程需要同步訪問,請在請求結構中包含事件或信號量,請求線程在推送請求后將等待。 flashWriter可以在完成時(或在加載帶有錯誤指示的結構:之后)發出信號。
主題有多種選擇-如果所有寫請求線程僅需要同步訪問,則它們可以使用自己的信號量保留自己的靜態請求結構,並僅將指向它的指針排隊。
使用生產者-消費者隊列類,該類允許在隊列的開頭進行高優先級推送,並且在運行powerfail時,在隊列的前面推送“ stopWriting”請求。 然后,flashWriter將完成正在進行的所有寫操作,彈出stopWriting請求,並被指示暫停自身(或者您可以使用flashWriter每次嘗試彈出隊列之前都檢查的“ stop”易失布爾值)。
這樣可以通過從其他線程中推送的閃存寫入請求中刪除硬互斥鎖來防止死鎖。 其他線程是否繼續排隊寫請求也沒關系-它們將永遠不會執行。
編輯:我剛喝了兩杯咖啡,考慮到這一點,“ flashWriter”線程可以輕松地成為“ FlashWriterAndPowerFail”線程:
如果設置了可變的“停止”布爾值,則無論隊列中是否有條目,都可以安排生產者-消費者隊列返回pop()結果為null。 在“ FWAPF”線程中,在每次pop()返回之后執行空檢查,如果不是,則對空或flashWrite操作執行powerFail操作。
當發生powerFail中斷時,請設置停止布爾值並在隊列中發出“計數”信號燈,以確保FWAPF線程當前在隊列中被阻塞時使其處於運行狀態。
這樣,您就不需要單獨的'powerFail'線程和堆棧-一個線程可以執行flashWrite和powerFail,同時仍確保沒有互斥鎖死鎖。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.