最近在開發STM32專案中出現了信號量互動例外的問題,該專案搭載的是FreeRtos系統,具體內容如下:
現象:
在系統運行程序中,出現如下斷言錯誤:
![]()
問題排查:
找到代碼斷言定義處如下:
#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); printf("assert: %s->%d\n", (__FUNCTION__), (__LINE__));for( ;; ); }
由日志可以知道,是系統出現了信號量在執行獲取程序中錯誤,
沿著錯誤繼續追蹤代碼的上層呼叫,很快可以找到對應的configASSERT出錯地方,如下:
/* Cannot block if the scheduler is suspended. */
#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
{
configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
}
#endif
根據configASSERT的定義可知,只有括號中的x等于0,才會報錯,可得出代碼運行到此處:
xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED 成立,說明調度器當前狀態處于
掛起態,xTaskGetSchedulerState() 函式是用于獲取當前調度器狀態,
xTicksToWait != 0 成立, 說明傳遞的入口引數信號量等待時間不為0,
綜上分析,并結合FreeRtos系統代碼注釋 /* Cannot block if the scheduler is suspended. 如果計
劃程式已掛起,則無法阻止 */
根據我們對信號量實作功能的認知:信號量可以用來實作對共享資源的互斥訪問和多任務之間的
同步,
由此結合分析可以知道:信號量的獲取和釋放操作運行在多任務的運行狀態下,當正要執行
xQueueSemaphoreTake進行信號量獲取操作的時候,任務調度器且是處于掛起狀態,這是不被允
許的,
重點:當調度器處于掛起狀態時,是不能呼叫FreeRtos API函式的
那到底是什么操作使得此時的調度器被掛起了呢?
可以通過結合日志出錯地方的背景關系查看,找到引起調度器掛起的使用地方,也就是
vTaskSuspendAll()使用,
void vTaskSuspendAll( void )
{
/* A critical section is not required as the variable is of type
BaseType_t. Please read Richard Barry's reply in the following link to a
post in the FreeRTOS support forum before reporting this as a bug! -
http://goo.gl/wu4acr */
++uxSchedulerSuspended;
}
通過一步步排查定位到是FreeRtos記憶體分配函式pvPortMalloc引起的調度器掛起,在記憶體分配過
程中,會先呼叫vTaskSuspendAll掛起調度器,分配完成后在呼叫xTaskResumeAll()開啟調度器
void *pvPortMalloc( size_t xWantedSize )
{
void *pvReturn = NULL;
static uint8_t *pucAlignedHeap = NULL;
/* Ensure that blocks are always aligned to the required number of bytes. */
#if( portBYTE_ALIGNMENT != 1 )
{
if( xWantedSize & portBYTE_ALIGNMENT_MASK )
{
/* Byte alignment required. */
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
}
}
#endif
vTaskSuspendAll();
{
if( pucAlignedHeap == NULL )
{
/* Ensure the heap starts on a correctly aligned boundary. */
pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
}
/* Check there is enough room left for the allocation. */
if( ( ( xNextFreeByte + xWantedSize ) < configADJUSTED_HEAP_SIZE ) &&
( ( xNextFreeByte + xWantedSize ) > xNextFreeByte ) )/* Check for overflow. */
{
/* Return the next free byte then increment the index past this
block. */
pvReturn = pucAlignedHeap + xNextFreeByte;
xNextFreeByte += xWantedSize;
}
traceMALLOC( pvReturn, xWantedSize );
}
( void ) xTaskResumeAll();
#if( configUSE_MALLOC_FAILED_HOOK == 1 )
{
if( pvReturn == NULL )
{
extern void vApplicationMallocFailedHook( void );
vApplicationMallocFailedHook();
}
}
#endif
return pvReturn;
}
由于掛起調度器實作的臨界區只可以保護一段代碼區間不被其他任務打斷,而此時中斷是使能
的,也就是可以被中斷打斷,加上頻繁使用pvPortMalloc分配和釋放使用不固定大小的記憶體空間過
程中,容易出現在掛起調度器時被中斷打斷后,在其他地方進行信號量操作,而導致出現以上問
題,
解決方法:
1.可以將頻繁分配釋放不固定記憶體大小的pvPortMalloc操作改為每次都分配釋放同樣大小的記憶體空
間,這樣也有利于減小記憶體碎片發生
2.也可選擇將你需要經常分配釋放那塊記憶體改為使用定義的一塊全域的陣列空間,而不用動態分配
的記憶體空間
在操作FreeRtos系統程序中應該注意事項:
1.互斥量不能在中斷服務函式中使用,因為其特有的優先級繼承機制只在任務起作用,在中斷的上
下文環境毫無意義
2.FreeRtos系統對應的一些功能介面函式是有區分用于中斷的介面和非中斷中使用的介面,如果
在中斷函式里呼叫了非中斷的系統介面,就會出現系統性斷言錯誤
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/262463.html
標籤:其他
下一篇:02_日期累加
