1 前言
最近博主在做一些適配freeRTOS的項目,簡單來說就是從別的RTOS平臺遷移到freeRTOS平臺。 由于之前的代碼都是可用的,憑經(jīng)驗我們認為只需要將OSAL的接口重新封裝一下,理論上上層的邏輯應(yīng)該問題不大;但是我們沒想到的卻是在OSAL層適配的時候,遇到了一些之前沒有考慮到的問題。
2 遇到的問題
簡單描述一下,我所遇到的問題;這個問題主要的體現(xiàn)就是在創(chuàng)建任務(wù)xTaskCreate
的接口調(diào)用上,freeRTOS的接口原型為:
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
const configSTACK_DEPTH_TYPE usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask ) PRIVILEGED_FUNCTION;
我在一個任務(wù)里面調(diào)用該接口去創(chuàng)建一個新的任務(wù),然后我發(fā)現(xiàn)新建的任務(wù)跑起來了,但是我發(fā)起創(chuàng)建的任務(wù)卻不往下跑了。初步觀察,就是xTaskCreate
接口沒有返回出來。
3 問題分析
3.1 初略分析
考慮到我的操作場景是在從別的RTOS平臺遷移代碼到freeRTOS平臺,所以第一感覺會是OSAL層封裝是不是有問題?
于是把任務(wù)創(chuàng)建相關(guān)的OSAL接口重新捋了一遍,包括每個參數(shù)的傳參轉(zhuǎn)換是否正確,都做了一個遍,確認封裝是沒有問題的。 之前我們做代碼調(diào)試的時候,留了一手,就是在xTaskCreate
內(nèi)部創(chuàng)建任務(wù)的時候,會把任務(wù)相關(guān)的一些信息打印出來,以便觀察。
/* Allocate space for the TCB. */
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e9087 !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack, and the first member of TCB_t is always a pointer to the task's stack. */
if( pxNewTCB != NULL )
{
/* Store the stack location in the TCB. */
pxNewTCB->pxStack = pxStack;
kprintf("[THD]%s:[tcb]%x [stack]%x-%x:%d:%d\r\n", pcName,
pxNewTCB, pxStack,
(size_t)pxStack + ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ),
( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) )
, uxPriority);
}
else
{
/* The stack cannot be used as the TCB was not created. Free
* it again. */
vPortFreeStack( pxStack );
}
所以這個調(diào)試信息,讓我發(fā)現(xiàn)了端倪,我在調(diào)用創(chuàng)建任務(wù)卡死不返回
的地方,發(fā)現(xiàn)了如下log:
[THD]cli:[tcb]41a360 [stack]419758-41a358:3072:60
任務(wù)優(yōu)先級為60?好像freeRTOS不支持這么高數(shù)值的優(yōu)先級? 到這里,初步懷疑是任務(wù)優(yōu)先級數(shù)值的問題導(dǎo)致的。
3.2 深究源碼
于是一步步去深究freerTOS的源碼,這就是開源代碼的好處??! 捋了一下代碼調(diào)用的關(guān)鍵路徑:
xTaskCreate ->
prvInitialiseNewTask ->
prvAddNewTaskToReadyList ->
其中在prvInitialiseNewTask
中有以下代碼片段:
/* This is used as an array index so must ensure it's not too large. */
configASSERT( uxPriority < configMAX_PRIORITIES );
if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
{
uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
pxNewTCB->uxPriority = uxPriority;
這段代碼主要是對任務(wù)優(yōu)先級數(shù)值的檢查和處理,這里可以看到它會跟configMAX_PRIORITIES
進行比較,比如我的環(huán)境下,這個值是10,它是在freeRTOSConfig.h里面定義的。 從這段代碼可以知道,應(yīng)用層傳入的60優(yōu)先級實際被修改成9了;在freeRTOS里面,這是最高優(yōu)先級了。
/* Task */
#define configMAX_PRIORITIES ( 10 )
在freeRTOS里面,這個值可以定義大一些,但是需要多消耗一些RAM。
然后在prvAddNewTaskToReadyList
中有以下代碼片段:
if( xSchedulerRunning != pdFALSE )
{
/* If the created task is of a higher priority than the current task
* then it should run now. */
if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority )
{
taskYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
這里尤其注意第2個if語句,如果創(chuàng)建的新任務(wù)優(yōu)先級更高,那么就立即執(zhí)行它
:
#define portYIELD() __asm ( "SWI 0" )
#ifndef portYIELD_WITHIN_API
#define portYIELD_WITHIN_API portYIELD
#endif
#if ( configUSE_PREEMPTION == 0 )
/* If the cooperative scheduler is being used then a yield should not be
* performed just because a higher priority task has been woken. */
#define taskYIELD_IF_USING_PREEMPTION()
#else
#define taskYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API()
#endif
在我的平臺,直接就跑到SWI0
了,及產(chǎn)生一個軟中斷,隨后就立即發(fā)生任務(wù)調(diào)度了。 那么這個時候,這個低優(yōu)先級的任務(wù)(調(diào)用了xTaskCreate的任務(wù))感覺就沒法往下執(zhí)行了,因為log都不打了!
3.3 代碼驗證
其實通過debug信息,我只需要在xTaskCreate的入口和出口加上調(diào)試信息就可以知道到底退沒退出,但是我無法驗證是否真的是:高優(yōu)先級的任務(wù)創(chuàng)建低優(yōu)先級的任務(wù)可以退出,而低優(yōu)先級的任務(wù)創(chuàng)建高優(yōu)先級的任務(wù)無法退出
。 于是我寫了以下代碼做個簡單驗證:
#include "task.h"
TaskHandle_t calling_task;
TaskHandle_t lower_task;
TaskHandle_t higher_task;
void create_lower_task(void *data)
{
TaskStatus_t TaskStatus;
vTaskGetInfo( lower_task,
&TaskStatus,
0,
eInvalid);
while(1) {
task_debug("%s:%d >>> prio: %d\r\n", __func__, __LINE__, TaskStatus.uxCurrentPriority);
vTaskDelay(1000);
}
}
void create_higher_task(void *data)
{
TaskStatus_t TaskStatus;
vTaskGetInfo( higher_task,
&TaskStatus,
0,
eInvalid);
while(1) {
task_debug("%s:%d >>> prio: %d\r\n", __func__, __LINE__, TaskStatus.uxCurrentPriority);
vTaskDelay(1000);
}
}
void create_calling_task(void *data)
{
int ret;
int lower_prio = 3;
int higher_prio = 60; //final set to prio 9
TaskStatus_t TaskStatus;
(void)higher_prio;
vTaskGetInfo( lower_task,
&TaskStatus,
0,
eInvalid);
task_debug("%s:%d >>> prio: %d\r\n", __func__, __LINE__, TaskStatus.uxCurrentPriority);
ret = xTaskCreate(create_lower_task, "test-2", 256, NULL, lower_prio,
(TaskHandle_t * const )&lower_task);
if (ret != pdPASS) {
task_debug("Error: Failed to create test task: %d\r\n", ret);
}
task_debug("%s:%d >>>\r\n", __func__, __LINE__);
#if 1
task_debug("%s:%d >>>\r\n", __func__, __LINE__);
ret = xTaskCreate(create_higher_task, "test-3", 256, NULL, higher_prio,
(TaskHandle_t * const )&higher_task);
if (ret != pdPASS) {
task_debug("Error: Failed to create test task: %d\r\n", ret);
}
task_debug("%s:%d >>>\r\n", __func__, __LINE__);
#endif
while(1) {
task_debug("%s:%d >>> prio: %d\r\n", __func__, __LINE__, TaskStatus.uxCurrentPriority);
vTaskDelay(1000);
}
}
void freertos_task_priority_test(void)
{
int ret;
int cur_prio = 4;
{
TaskStatus_t TaskStatus;
vTaskGetInfo( xTaskGetHandle( "extended_app" ),
&TaskStatus,
0,
eInvalid);
task_debug("%s:%d >>> prio: %d\r\n", __func__, __LINE__, TaskStatus.uxCurrentPriority);
}
task_debug("%s:%d >>>\r\n", __func__, __LINE__);
ret = xTaskCreate(create_calling_task, "test-1", 256, NULL, cur_prio,
(TaskHandle_t * const )&calling_task);
if (ret != pdPASS) {
cli_printf("Error: Failed to create test task: %d\r\n", ret);
}
task_debug("%s:%d >>>\r\n", __func__, __LINE__);
}
#endif
結(jié)果代碼一跑,卻超出了我之前的預(yù)想:
freertos_task_priority_test:793 >>> prio: 4
freertos_task_priority_test:796 >>>
[THD]test-1:[tcb]419b68 [stack]419760-419b60:1024:4
freercreate_calling_task:758tos_task_priority_test: >>> prio: 4
[THD]test802 >>>
[THD]cli:[tcb]-2:[tcb]41a1e8 [stack]4419bd8 [stack]41a258-4119de0-41a1e0:1024:3
creae58:3072:60
ate_calling_task:764 >>>
create_calling_task:766 >>>
[THD]test-3:[tcb]419c48 [stack]41ae60-41b260:1024:60
create_higher_task:739 >>> prio: 9
create_calling_task:772 >>>
create_calling_task:776 >>> prio: 4
create_lower_task:724 >>> prio: 3
create_higher_task:739 >>> prio: 9
create_calling_task:776 >>> prio: 4
create_lower_task:724 >>> prio: 3
create_higher_task:739 >>> prio: 9
create_calling_task:776 >>> prio: 4
create_lower_task:724 >>> prio: 3
create_higher_task:739 >>> prio: 9
create_calling_task:776 >>> prio: 4
create_lower_task:724 >>> prio: 3
create_higher_task:739 >>> prio: 9
create_calling_task:776 >>> prio: 4
create_lower_task:724 >>> prio: 3
create_higher_task:739 >>> prio: 9
create_calling_task:776 >>> prio: 4
create_lower_task:724 >>> prio: 3
create_higher_task:739 >>> prio: 9
create_calling_task:776 >>> prio: 4
create_lower_task:724 >>> prio: 3
create_higher_task:739 >>> prio: 9
create_calling_task:776 >>> prio: 4
create_lower_task:724 >>> prio: 3
簡單來說,就是xTaskCreate
壓根就沒有卡住啊? 一個任務(wù)優(yōu)先級為4的任務(wù),分別創(chuàng)建任務(wù)優(yōu)先級為3和9的任務(wù),都跑的好好的,并沒有發(fā)現(xiàn)xTaskCreate不返回
的問題! 難道是我哪里想錯了?
3.4 進一步分析
通過上面的簡單代碼已經(jīng)驗證了,我之前的猜想是不對的,但是我的確看到了在我的應(yīng)用代碼里面出現(xiàn)了xTaskCreate卡死不返回的情況,還需要再細細分析下我新創(chuàng)建的這個任務(wù),多半是問題出在它身上,因為我屏蔽了創(chuàng)建它,問題就沒有復(fù)現(xiàn)了。 為了說明問題,我把這個任務(wù)的執(zhí)行代碼簡略了下:
void task_main(void *data)
{
int32_t ret;
char *msg = NULL;
while (!task_cancel_check()) {
if (task_get_input(g_cli->inbuf, &g_cli->bp) != 0) {
/* do something */
}
}
task_exit();
}
看到這偽代碼,也許你發(fā)現(xiàn)了點問題,這個while里面看樣子都是查詢下的代碼,并且沒有延時處理,如果這個任務(wù)的優(yōu)先級是最高的,那么它將一直占用CPU,別的任務(wù)壓根無法被調(diào)度到。 回想我的代碼場景,傳入了一個優(yōu)先級60,被減小到configMAX_PRIORITIES-1
,即優(yōu)先級為9;這個在freeRTOS里面可是最高優(yōu)先級的任務(wù)了,所以才出現(xiàn)了xTaskCreate
無法退出返回;因為這個時候除這個最高優(yōu)先級的任務(wù)在跑外,其他任務(wù)都可能跑不起來了。
3.5 如何優(yōu)化
明白了出現(xiàn)問題的原因,修改一來就很簡單了,這里提供兩個思路:
1) 把這個taskmain修改成一個合適的優(yōu)先級,像這個不緊急的任務(wù),建議設(shè)置成次優(yōu)先級即可,即優(yōu)先級數(shù)值為1; 2)在taskmain的while循環(huán)里面,加上適當(dāng)?shù)膁elay,比如vTaskDelay(1)
,讓其在合適的時間讓出CPU。
理論上,以上兩種方案都可以解決問題,但是肯定強烈推薦方式1,因為它才是解決根源的思路。
4 經(jīng)驗總結(jié)
- freeRTOS的優(yōu)先級定義與別人不一樣,不要混淆!
- 在freeRTOS里面創(chuàng)建任務(wù),注意考量下優(yōu)先級的問題,不能隨意定義優(yōu)先級!
- 高優(yōu)先級的任務(wù)里面創(chuàng)建低優(yōu)先級的任務(wù)是可以的;但是反過來,低優(yōu)先級任務(wù)里面創(chuàng)建高優(yōu)先級任務(wù),也是可以的!
- 編寫任務(wù)的執(zhí)行函數(shù),注意不要讓它死跑,如果沒有調(diào)用阻塞式的可以引起系統(tǒng)掛起的接口,適當(dāng)使用vTaskDelay接口讓任務(wù)讓出CPU。
5 更多分享
歡迎關(guān)注我的github倉庫01workstation,日常分享一些開發(fā)筆記和項目實戰(zhàn),歡迎指正問題。
同時也非常歡迎關(guān)注我的CSDN主頁和專欄:
【CSDN主頁:架構(gòu)師李肯】
【RT-Thread主頁:架構(gòu)師李肯】
【GCC專欄】
【信息安全專欄】
【RT-Thread開發(fā)筆記】
【freeRTOS開發(fā)筆記】
有問題的話,可以跟我討論,知無不答,謝謝大家。
審核編輯:湯梓紅
-
接口
+關(guān)注
關(guān)注
33文章
8605瀏覽量
151194 -
RTOS
+關(guān)注
關(guān)注
22文章
813瀏覽量
119649 -
FreeRTOS
+關(guān)注
關(guān)注
12文章
484瀏覽量
62182
發(fā)布評論請先 登錄
相關(guān)推薦
評論