Skip to content

优化

栈的使用情况

在创建任务时分配了栈,可以填入固定的数值比如0xa5,以后可以使用以下函数查看"栈的高水位",也就是还有多少空余的栈空间:

c
UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );

原理是:从栈底往栈顶逐个字节地判断,它们的值持续是0xa5就表示它是空闲的。

任务运行时、任务被切换时,都会用到栈。栈里原来值(0xa5)就会被覆盖。逐个函数从栈的尾部判断栈的值连续为0xa5的个数,它就是任务运行过程中空闲内存容量的最小值。注意:假设从栈尾开始连续为0xa5的栈空间是N字节,返回值是N/4。

任务的运行时间统计

对于同优先级的任务,它们按照时间片轮流运行:你执行一个Tick,我执行一个Tick。

是否可以在Tick中断函数中,统计当前任务的累计运行时间?

不行!很不精确,因为有更高优先级的任务就绪时,当前任务还没运行一个完整的Tick就被抢占了。

我们需要比Tick更快的时钟,比如Tick周期时1ms,我们可以使用另一个定时器,让它发生中断的周期时0.1ms甚至更短。

使用这个定时器来衡量一个任务的运行时间

image-20231123200019498

  • 切换到Task1时,使用更快的定时器记录当前时间T1
  • Task1被切换出去时,使用更快的定时器记录当前时间T4
  • (T4-T1)就是它运行的时间,累加起来
  • 关键点:在vTaskSwitchContext函数中,使用更快的定时器统计运行时间

还可以使用一个变量count记录当前的时钟中断的个数, 在任务切换的时候记录Timer的值, 在加上之前记录count从而获得精确地数值

配置

c
#define configGENERATE_RUN_TIME_STATS 1
//初始化这一个以后需要再定义一个初始化我们使用的时钟的函数
#define configCONFIGURE_TIMER_FOR_RUN_TIME_STATS Timer_Init
//一个获取这个时间的函数
/*
    #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE
    //记录总的运行时间
    portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime );
    #else
    ulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE();
    #endif
*/
#define configUSE_TRACE_FACILITY    1
#define configUSE_STATS_FORMATTING_FUNCTIONS  1
  • 实现宏portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(),它用来初始化更快的定时器
  • 实现这两个宏之一,它们用来返回当前时钟值(更快的定时器)
    • portGET_RUN_TIME_COUNTER_VALUE():直接返回时钟值
    • portALT_GET_RUN_TIME_COUNTER_VALUE(Time):设置Time变量等于时钟值

可以使用初始化一个外设时钟, 之后使用这一个时钟进行获取时间

c
#if ( configGENERATE_RUN_TIME_STATS == 1 )
{
    #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE
    //记录总的运行时间
    portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime );
    #else
    ulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE();
    #endif

    /* Add the amount of time the task has been running to the
     * accumulated time so far.  The time the task started running was
     * stored in ulTaskSwitchedInTime.  Note that there is no overflow
     * protection here so count values are only valid until the timer
     * overflows.  The guard against negative values is to protect
     * against suspect run time stat counter implementations - which
     * are provided by the application, not the kernel. */
    if( ulTotalRunTime > ulTaskSwitchedInTime )
    {
        pxCurrentTCB->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime );
    }//把这一次的运行时间进行累加
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

    ulTaskSwitchedInTime = ulTotalRunTime;
}
#endif /* configGENERATE_RUN_TIME_STATS */

在切换任务的时候如果有这一个宏

信息获取

获得统计信息,可以使用下列函数

  • uxTaskGetSystemState:对于每个任务它的统计信息都放在一个TaskStatus_t结构体里

  • vTaskList:得到的信息是可读的字符串,比如

  • vTaskGetRunTimeStats: 得到的信息是可读的字符串,比如

  • uxTaskGetSystemState:获得任务的统计信息

c
UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray,
                                        const UBaseType_t uxArraySize,
                                        uint32_t * const pulTotalRunTime );
参数描述
pxTaskStatusArray指向一个TaskStatus_t结构体数组,用来保存任务的统计信息。
有多少个任务?可以用uxTaskGetNumberOfTasks()来获得。
uxArraySize数组大小、数组项个数,必须大于或等于uxTaskGetNumberOfTasks()
pulTotalRunTime用来保存当前总的运行时间(更快的定时器),可以传入NULL
返回值传入的pxTaskStatusArray数组,被设置了几个数组项。
注意:如果传入的uxArraySize小于uxTaskGetNumberOfTasks(),返回值就是0 vTaskList :获得任务的统计信息,形式为可读的字符串。注意,pcWriteBuffer必须足够大。
  • vTaskList :获得任务的统计信息,形式为可读的字符串。注意,pcWriteBuffer必须足够大。
c
  void vTaskList( signed char *pcWriteBuffer );

可读信息格式如下:

image-20231123201628469

  • vTaskGetRunTimeStats:获得任务的运行信息,形式为可读的字符串。注意,pcWriteBuffer必须足够大。
c
void vTaskGetRunTimeStats( signed char *pcWriteBuffer );

可读信息格式如下:

image-20231123201642764