【freeRTOS开发笔记】为什么xTaskCreate接口会卡死不返回

文章目录


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 如何优化

明白了出现问题的原因,修改一来就很简单了,这里提供两个思路:

  1. 把这个task_main修改成一个合适的优先级,像这个不紧急的任务,建议设置成次优先级即可,即优先级数值为1;
    2)在task_main的while循环里面,加上适当的delay,比如vTaskDelay(1),让其在合适的时间让出CPU。

理论上,以上两种方案都可以解决问题,但是肯定强烈推荐方式1,因为它才是解决根源的思路。

4 经验总结

  • freeRTOS的优先级定义与别人不一样,不要混淆!
  • 在freeRTOS里面创建任务,注意考量下优先级的问题,不能随意定义优先级!
  • 高优先级的任务里面创建低优先级的任务是可以的;但是反过来,低优先级任务里面创建高优先级任务,也是可以的!
  • 编写任务的执行函数,注意不要让它死跑,如果没有调用阻塞式的可以引起系统挂起的接口,适当使用vTaskDelay接口让任务让出CPU。

5 更多分享

欢迎关注我的github仓库01workstation,日常分享一些开发笔记和项目实战,欢迎指正问题。

同时也非常欢迎关注我的CSDN主页和专栏:

【http://yyds.recan-li.cn】

【C/C++语言编程专栏】

【GCC专栏】

【信息安全专栏】

【RT-Thread开发笔记】

【freeRTOS开发笔记】

有问题的话,可以跟我讨论,知无不答,谢谢大家。

上一篇:c++中用new动态创建对象注意事项


下一篇:TCS学习笔记简介