文章目录
1 前言
最近博主在做一些适配freeRTOS的项目,简单来说就是从别的RTOS平台迁移到freeRTOS平台。
由于之前的代码都是可用的,凭经验我们认为只需要将OSAL的接口重新封装一下,理论上上层的逻辑应该问题不大;但是我们没想到的却是在OSAL层适配的时候,遇到了一些之前没有考虑到的问题。
2 遇到的问题
简单描述一下,我所遇到的问题;这个问题主要的体现就是在创建任务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
接口没有返回出来。
3 问题分析
3.1 初略分析
考虑到我的操作场景是在从别的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不支持这么高数值的优先级?
到这里,初步怀疑是任务优先级数值的问题导致的。
3.2 深究源码
于是一步步去深究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都不打了!
3.3 代码验证
其实通过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不返回
的问题!
难道是我哪里想错了?
3.4 进一步分析
通过上面的简单代码已经验证了,我之前的猜想是不对的,但是我的确看到了在我的应用代码里面出现了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
无法退出返回;因为这个时候除这个最高优先级的任务在跑外,其他任务都可能跑不起来了。
3.5 如何优化
明白了出现问题的原因,修改一来就很简单了,这里提供两个思路:
- 把这个task_main修改成一个合适的优先级,像这个不紧急的任务,建议设置成次优先级即可,即优先级数值为1;
2)在task_main的while循环里面,加上适当的delay,比如vTaskDelay(1)
,让其在合适的时间让出CPU。
理论上,以上两种方案都可以解决问题,但是肯定强烈推荐方式1,因为它才是解决根源的思路。
4 经验总结
- freeRTOS的优先级定义与别人不一样,不要混淆!
- 在freeRTOS里面创建任务,注意考量下优先级的问题,不能随意定义优先级!
- 高优先级的任务里面创建低优先级的任务是可以的;但是反过来,低优先级任务里面创建高优先级任务,也是可以的!
- 编写任务的执行函数,注意不要让它死跑,如果没有调用阻塞式的可以引起系统挂起的接口,适当使用vTaskDelay接口让任务让出CPU。
5 更多分享
欢迎关注我的github仓库01workstation,日常分享一些开发笔记和项目实战,欢迎指正问题。
同时也非常欢迎关注我的CSDN主页和专栏:
有问题的话,可以跟我讨论,知无不答,谢谢大家。