最近博主在做一些适配freeRTOS的项目,简单来说就是从别的RTOS平台迁移到freeRTOS平台。
由于之前的代码都是可用的,凭经验我们认为只需要将OSAL的接口重新封装一下,理论上上层的逻辑应该问题不大;但是我们没想到的却是在OSAL层适配的时候,遇到了一些之前没有考虑到的问题。
简单描述一下,我所遇到的问题;这个问题主要的体现就是在创建任务xTaskCreate
的接口调用上,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;
我在一个任务里面调用该接口去创建一个新的任务,然后我发现新建的任务跑起来了,但是我发起创建的任务却不往下跑了。初步观察,就是xTaskCreate
接口没有返回出来。
考虑到我的操作场景是在从别的RTOS平台迁移代码到freeRTOS平台,所以第一感觉会是OSAL层封装是不是有问题?
于是把任务创建相关的OSAL接口重新捋了一遍,包括每个参数的传参转换是否正确,都做了一个遍,确认封装是没有问题的。
之前我们做代码调试的时候,留了一手,就是在xTaskCreate
内部创建任务的时候,会把任务相关的一些信息打印出来,以便观察。
/* 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 ); }
所以这个调试信息,让我发现了端倪,我在调用创建任务卡死不返回
的地方,发现了如下log:
[THD]cli:[tcb]41a360 [stack]419758-41a358:3072:60
任务优先级为60?好像freeRTOS不支持这么高数值的优先级?
到这里,初步怀疑是任务优先级数值的问题导致的。
于是一步步去深究freerTOS的源码,这就是开源代码的好处啊!
捋了一下代码调用的关键路径:
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;
这段代码主要是对任务优先级数值的检查和处理,这里可以看到它会跟configMAX_PRIORITIES
进行比较,比如我的环境下,这个值是10,它是在freeRTOSConfig.h里面定义的。
从这段代码可以知道,应用层传入的60优先级实际被修改成9了;在freeRTOS里面,这是最高优先级了。
/* 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语句,如果创建的新任务优先级更高,那么就立即执行它
:
#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
在我的平台,直接就跑到SWI 0
了,及产生一个软中断,随后就立即发生任务调度了。
那么这个时候,这个低优先级的任务(调用了xTaskCreate的任务)感觉就没法往下执行了,因为log都不打了!
其实通过debug信息,我只需要在xTaskCreate的入口和出口加上调试信息就可以知道到底退没退出,但是我无法验证是否真的是:高优先级的任务创建低优先级的任务可以退出,而低优先级的任务创建高优先级的任务无法退出
。
于是我写了以下代码做个简单验证:
#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
结果代码一跑,却超出了我之前的预想:
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
压根就没有卡住啊?
一个任务优先级为4的任务,分别创建任务优先级为3和9的任务,都跑的好好的,并没有发现xTaskCreate不返回
的问题!
难道是我哪里想错了?
通过上面的简单代码已经验证了,我之前的猜想是不对的,但是我的确看到了在我的应用代码里面出现了xTaskCreate卡死不返回的情况,还需要再细细分析下我新创建的这个任务,多半是问题出在它身上,因为我屏蔽了创建它,问题就没有复现了。
为了说明问题,我把这个任务的执行代码简略了下:
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(); }
看到这伪代码,也许你发现了点问题,这个while里面看样子都是查询下的代码,并且没有延时处理,如果这个任务的优先级是最高的,那么它将一直占用CPU,别的任务压根无法被调度到。
回想我的代码场景,传入了一个优先级60,被减小到configMAX_PRIORITIES-1
,即优先级为9;这个在freeRTOS里面可是最高优先级的任务了,所以才出现了xTaskCreate
无法退出返回;因为这个时候除这个最高优先级的任务在跑外,其他任务都可能跑不起来了。
明白了出现问题的原因,修改一来就很简单了,这里提供两个思路:
vTaskDelay(1)
,让其在合适的时间让出CPU。理论上,以上两种方案都可以解决问题,但是肯定强烈推荐方式1,因为它才是解决根源的思路。
欢迎关注我的github仓库01workstation,日常分享一些开发笔记和项目实战,欢迎指正问题。
同时也非常欢迎关注我的CSDN主页和专栏:
【http://yyds.recan-li.cn】
【C/C++语言编程专栏】
【GCC专栏】
【信息安全专栏】
【RT-Thread开发笔记】
【freeRTOS开发笔记】
有问题的话,可以跟我讨论,知无不答,谢谢大家。