What does volatile do?

 What does volatile do?

This is probably best explained by comparing the effects that volatile and synchronized have on a method. volatile is a field modifier, while synchronized modifies code blocks and methods. So we can specify three variations of a simple accessor using those two keywords:

         int i1;              int geti1() {return i1;}
volatile int i2;              int geti2() {return i2;}
         int i3; synchronized int geti3() {return i3;}

geti1() accesses the value currently stored in i1 in the current thread. Threads can have local copies of variables, and the data does not have to be the same as the data held in other threads. In particular, another thread may have updated i1 in it's thread, but the value in the current thread could be different from that updated value. In fact Java has the idea of a "main" memory, and this is the memory that holds the current "correct" value for variables. Threads can have their own copy of data for variables, and the thread copy can be different from the "main" memory. So in fact, it is possible for the "main" memory to have a value of 1 for i1, for thread1 to have a value of 2 for i1 and for thread2 to have a value of 3 for i1 if thread1 and thread2 have both updated i1 but those updated value has not yet been propagated to "main" memory or other threads.

On the other hand, geti2() effectively accesses the value of i2 from "main" memory. A volatile variable is not allowed to have a local copy of a variable that is different from the value currently held in "main" memory. Effectively, a variable declared volatile must have it's data synchronized across all threads, so that whenever you access or update the variable in any thread, all other threads immediately see the same value. Of course, it is likely that volatile variables have a higher access and update overhead than "plain" variables, since the reason threads can have their own copy of data is for better efficiency.

Well if volatile already synchronizes data across threads, what is synchronized for? Well there are two differences. Firstly synchronized obtains and releases locks on monitors which can force only one thread at a time to execute a code block, if both threads use the same monitor (effectively the same object lock). That's the fairly well known aspect to synchronized. But synchronized also synchronizes memory. In fact synchronized synchronizes the whole of thread memory with "main" memory. So executing geti3() does the following:

 

  1. The thread acquires the lock on the monitor for object this (assuming the monitor is unlocked, otherwise the thread waits until the monitor is unlocked).
  2. The thread memory flushes all its variables, i.e. it has all of its variables effectively read from "main" memory (JVMs can use dirty sets to optimize this so that only "dirty" variables are flushed, but conceptually this is the same. See section 17.9 of the Java language specification).
  3. The code block is executed (in this case setting the return value to the current value of i3, which may have just been reset from "main" memory).
  4. (Any changes to variables would normally now be written out to "main" memory, but for geti3() we have no changes.)
  5. The thread releases the lock on the monitor for object this.

So where volatile only synchronizes the value of one variable between thread memory and "main" memory, synchronized synchronizes the value of all variables between thread memory and "main" memory, and locks and releases a monitor to boot. Clearly synchronized is likely to have more overhead than volatile.

#if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) void vTaskGetRunTimeStats( char *pcWriteBuffer ) { TaskStatus_t *pxTaskStatusArray; volatile UBaseType_t uxArraySize, x; uint32_t ulTotalTime, ulStatsAsPercentage; #if( configUSE_TRACE_FACILITY != 1 ) { #error configUSE_TRACE_FACILITY must also be set to 1 in FreeRTOSConfig.h to use vTaskGetRunTimeStats(). } #endif /* * PLEASE NOTE: * * This function is provided for convenience only, and is used by many * of the demo applications. Do not consider it to be part of the * scheduler. * * vTaskGetRunTimeStats() calls uxTaskGetSystemState(), then formats part * of the uxTaskGetSystemState() output into a human readable table that * displays the amount of time each task has spent in the Running state * in both absolute and percentage terms. * * vTaskGetRunTimeStats() has a dependency on the sprintf() C library * function that might bloat the code size, use a lot of stack, and * provide different results on different platforms. An alternative, * tiny, third party, and limited functionality implementation of * sprintf() is provided in many of the FreeRTOS/Demo sub-directories in * a file called printf-stdarg.c (note printf-stdarg.c does not provide * a full snprintf() implementation!). * * It is recommended that production systems call uxTaskGetSystemState() * directly to get access to raw stats data, rather than indirectly * through a call to vTaskGetRunTimeStats(). */ /* Make sure the write buffer does not contain a string. */ *pcWriteBuffer = 0x00; /* Take a snapshot of the number of tasks in case it changes while this function is executing. */ uxArraySize = uxCurrentNumberOfTasks; /* Allocate an array index for each task. NOTE! If configSUPPORT_DYNAMIC_ALLOCATION is set to 0 then pvPortMalloc() will equate to NULL. */ pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); if( pxTaskStatusArray != NULL ) { /* Generate the (binary) data. */ uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, &ulTotalTime ); /* For percentage calculations. */ ulTotalTime /= 100UL; /* Avoid divide by zero errors. */ if( ulTotalTime > 0 ) { /* Create a human readable table from the binary data. */ for( x = 0; x < uxArraySize; x++ ) { /* What percentage of the total run time has the task used? This will always be rounded down to the nearest integer. ulTotalRunTimeDiv100 has already been divided by 100. */ ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter / ulTotalTime; /* Write the task name to the string, padding with spaces so it can be printed in tabular form more easily. */ pcWriteBuffer = prvWriteNameToBuffer( pcWriteBuffer, pxTaskStatusArray[ x ].pcTaskName ); if( ulStatsAsPercentage > 0UL ) { #ifdef portLU_PRINTF_SPECIFIER_REQUIRED { sprintf( pcWriteBuffer, "\t%lu\t\t%lu%%\r\n", pxTaskStatusArray[ x ].ulRunTimeCounter, ulStatsAsPercentage ); } #else { /* sizeof( int ) == sizeof( long ) so a smaller printf() library can be used. */ sprintf( pcWriteBuffer, "\t%u\t\t%u%%\r\n", ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter, ( unsigned int ) ulStatsAsPercentage ); } #endif } else { /* If the percentage is zero here then the task has consumed less than 1% of the total run time. */ #ifdef portLU_PRINTF_SPECIFIER_REQUIRED { sprintf( pcWriteBuffer, "\t%lu\t\t<1%%\r\n", pxTaskStatusArray[ x ].ulRunTimeCounter ); } #else { /* sizeof( int ) == sizeof( long ) so a smaller printf() library can be used. */ sprintf( pcWriteBuffer, "\t%u\t\t<1%%\r\n", ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter ); } #endif } pcWriteBuffer += strlen( pcWriteBuffer ); } } else { mtCOVERAGE_TEST_MARKER(); } /* Free the array again. NOTE! If configSUPPORT_DYNAMIC_ALLOCATION is 0 then vPortFree() will be #defined to nothing. */ vPortFree( pxTaskStatusArray ); } else { mtCOVERAGE_TEST_MARKER(); } } 解释一下
06-18
<think>我们正在讨论FreeRTOS中的`vTaskGetRunTimeStats`函数,该函数用于获取任务的运行时统计信息。根据引用内容,我们可以整理出以下关键点:1.**功能**:`vTaskGetRunTimeStats`函数用于生成每个任务的运行时统计信息,包括任务运行时间占总时间的百分比等。这些信息对于性能分析和调试非常有用。2.**依赖配置宏**:-`configGENERATE_RUN_TIME_STATS`:必须设置为1以启用运行时统计功能。-`configUSE_TRACE_FACILITY`:必须设置为1以启用可视化跟踪调试功能(提供任务状态等信息)。-`configUSE_STATS_FORMATTING_FUNCTIONS`:必须设置为1以启用统计格式化函数(包括`vTaskGetRunTimeStats`函数本身)。3.**实现条件**:在`task.c`中,`vTaskGetRunTimeStats`函数的实现被条件编译包围,只有当上述三个宏都满足条件时才会被编译:```c#if((configGENERATE_RUN_TIME_STATS==1)&&(configUSE_STATS_FORMATTING_FUNCTIONS>0)&&(configUSE_TRACE_FACILITY==1))voidvTaskGetRunTimeStats(char*pcWriteBuffer){//函数实现}#endif```4.**运行时统计的实现机制**:-需要用户提供一个高精度定时器来记录任务运行时间。引用[1]中提到,需要在`FreeRTOSConfig.h`中定义`portCONFIGURE_TIMER_FOR_RUN_TIME_STATS`宏,并实现对应的`ConfigureTimeForRunTimeStats`函数(或其他用户命名的函数)来配置这个定时器。-通常,这个定时器需要比系统节拍定时器(tick)的精度更高,以便更精确地测量任务运行时间。-定时器需要提供一个计数值,该值会在任务切换时被读取并记录到任务的控制块(TCB)中。FreeRTOS使用这个计数值来计算任务实际运行的时间。5.**函数参数**:-`char*pcWriteBuffer`:指向一个字符缓冲区的指针,该缓冲区用于存储生成的统计信息字符串。这个缓冲区需要足够大,通常建议为每个任务预留至少40个字符的空间。6.**输出内容**:函数生成的字符串通常包含以下信息(以人类可读的文本形式):-任务名称-任务运行时间(绝对值,如计数器值)-任务运行时间占总运行时间的百分比7.**注意事项**:-启用运行时统计会增加额外的开销,因为每次任务切换时都需要读取定时器的计数值并更新统计信息。-高精度定时器的选择取决于具体的硬件平台,例如ARMCortex-M系列可以使用DWT周期计数器(CYCCNT)作为高精度定时器。###配置步骤总结1.在`FreeRTOSConfig.h`中设置必要的宏:```c#defineconfigGENERATE_RUN_TIME_STATS1#defineconfigUSE_TRACE_FACILITY1#defineconfigUSE_STATS_FORMATTING_FUNCTIONS1```2.配置高精度定时器:-定义宏`portCONFIGURE_TIMER_FOR_RUN_TIME_STATS`,将其指向配置定时器的函数(例如`ConfigureTimeForRunTimeStats`)。-实现定时器配置函数,初始化定时器(如使能DWT周期计数器)。-定义宏`portGET_RUN_TIME_COUNTER_VALUE`,用于获取当前定时器的计数值。###示例代码片段在`FreeRTOSConfig.h`中:```cexternvoidConfigureTimerForRunTimeStats(void);#defineportCONFIGURE_TIMER_FOR_RUN_TIME_STATS()ConfigureTimerForRunTimeStats()#defineportGET_RUN_TIME_COUNTER_VALUE()get_run_time_counter_value()```在某个C文件中(如`main.c`):```c//假设使用ARMCortex-M的DWT周期计数器voidConfigureTimerForRunTimeStats(void){CoreDebug->DEMCR|=CoreDebug_DEMCR_TRCENA_Msk;DWT->CYCCNT=0;DWT->CTRL|=DWT_CTRL_CYCCNTENA_Msk;}uint32_tget_run_time_counter_value(void){returnDWT->CYCCNT;}```###使用示例```ccharbuffer[512];//足够大的缓冲区vTaskGetRunTimeStats(buffer);printf("%s",buffer);//打印统计信息```###注意事项-定时器的位数要足够宽,避免快速溢出。例如32位的定时器在100MHz的系统时钟下大约43秒溢出一次,这可能导致统计错误。如果系统需要长时间运行,可能需要使用64位计数器或处理溢出情况。-运行时统计功能会增加任务切换的开销,因此在性能敏感的系统中使用时需谨慎。通过以上步骤,我们可以正确配置并使用`vTaskGetRunTimeStats`函数来获取任务的运行时统计信息。[^1]:引用[1]提到了配置定时器的宏和函数。[^2]:引用[2]介绍了与运行时统计相关的配置选项。[^3]:引用[3]说明了`vTaskGetRunTimeStats`函数的条件编译。§§相关问题§§1.如何为FreeRTOS选择合适的高精度定时器以实现运行时统计?2.如何解读`vTaskGetRunTimeStats`函数的输出结果?3.运行时统计功能对FreeRTOS系统性能有何影响?4.在32位定时器上如何防止溢出影响运行时统计?5.除了`vTaskGetRunTimeStats`,FreeRTOS还提供哪些任务状态查询函数?</think>### FreeRTOS `vTaskGetRunTimeStats` 函数详解 `vTaskGetRunTimeStats` 是 FreeRTOS 中用于获取任务运行时统计信息的关键函数,其功能和可用性依赖于特定的配置宏。以下是详细解析: #### 一、函数功能 ```c void vTaskGetRunTimeStats(char *pcWriteBuffer); ``` - **作用**:生成所有任务的运行时统计信息文本报告 - **输出格式**: ``` 任务名 运行时间(绝对计数) 占用CPU百分比 Task1 12345 25% IDLE 67890 75% ``` - **输出位置**:将格式化文本写入`pcWriteBuffer`指向的字符缓冲区 #### 二、依赖配置宏 必须同时启用以下三个宏才能使用该函数: | 配置宏 | 必需值 | 作用 | |--------|--------|------| | `configGENERATE_RUN_TIME_STATS` | **1** | 启用运行时统计功能 | | `configUSE_TRACE_FACILITY` | **1** | 启用可视化跟踪调试功能 | | `configUSE_STATS_FORMATTING_FUNCTIONS` | **1** | 启用统计格式化函数 | 在 `FreeRTOSConfig.h` 中的典型配置: ```c #define configGENERATE_RUN_TIME_STATS 1 // 启用运行时统计 #define configUSE_TRACE_FACILITY 1 // 启用跟踪设施 #define configUSE_STATS_FORMATTING_FUNCTIONS 1 // 启用格式化函数 ``` #### 三、底层依赖机制 1. **时间统计基础**: - 需要实现高精度计时器(通常 >10倍于系统时钟) - 配置计时器宏: ```c #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS ConfigureTimeForRunTimeStats ``` 其中 `ConfigureTimeForRunTimeStats` 是用户实现的计时器初始化函数[^1] 2. **计时器接口**: - 定义获取计时器值的宏: ```c #define portGET_RUN_TIME_COUNTER_VALUE() get_runtime_counter() ``` 返回当前计时器计数值(32位或64位) 3. **统计更新机制**: ```mermaid graph LR A[任务切换] --> B[记录当前计时器值T1] B --> C[计算上一任务运行时间] C --> D[更新任务TCB中的累计时间] D --> E[记录新任务开始时间T2] ``` #### 四、函数实现逻辑 在 `tasks.c` 中的条件编译块: ```c #if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && \ ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && \ ( configUSE_TRACE_FACILITY == 1 ) ) void vTaskGetRunTimeStats( char * pcWriteBuffer ) { // 1. 获取所有任务状态数组 pxTaskStatusArray = pvPortMalloc( uxArraySize * sizeof( TaskStatus_t ) ); // 2. 获取任务状态快照 uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, ... ); // 3. 计算总运行时间 ulTotalTime = 0; for(x=0; x<uxArraySize; x++) { ulTotalTime += pxTaskStatusArray[x].ulRunTimeCounter; } // 4. 生成格式化输出 for(x=0; x<uxArraySize; x++) { ulStatsAsPercentage = (100 * pxTaskStatusArray[x].ulRunTimeCounter) / ulTotalTime; sprintf(pcWriteBuffer, "%-12s%10u %10u%%\r\n", pxTaskStatusArray[x].pcTaskName, pxTaskStatusArray[x].ulRunTimeCounter, ulStatsAsPercentage); pcWriteBuffer += strlen(pcWriteBuffer); } // 5. 释放内存 vPortFree( pxTaskStatusArray ); } #endif ``` #### 五、使用示例 ```c void print_runtime_stats(void) { // 分配足够大的缓冲区(建议:40字节/任务) char *buffer = pvPortMalloc(512); // 获取统计信息 vTaskGetRunTimeStats(buffer); // 输出到串口 UART_Print(buffer); // 释放缓冲区 vPortFree(buffer); } ``` #### 六、注意事项 1. **内存需求**: - 每个任务需要约40字节输出空间 - 内部需要额外内存存储任务快照 2. **计时器要求**: - 精度必须高于系统时钟 - 推荐使用硬件定时器(如ARM的DWT Cycle Counter) 3. **系统开销**: - 每次任务切换增加约50个周期开销 - 函数执行期间会挂起调度器 4. **百分比计算**: $$ \text{任务占比} = \frac{\text{任务运行计数}}{\text{总运行计数}} \times 100\% $$ 总运行计数包括所有任务和空闲任务[^3] [^1]: 引用来源说明需要配置计时器宏 [^2]: 引用来源说明相关配置选项的作用 [^3]: 引用来源展示函数实现的条件编译块
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值