前言:
如今咱们对“堆栈使用率行业标准”大概比较注重,兄弟们都需要分析一些“堆栈使用率行业标准”的相关文章。那么小编在网摘上汇集了一些关于“堆栈使用率行业标准””的相关内容,希望咱们能喜欢,朋友们快快来学习一下吧!#单片机##嵌入式编程##RTOS##程序员#
1、前言
大部分从事STM32单片机嵌入式开发工作N年的人,项目也做了N个,是否曾经程序无缘无故跑飞了?系统无缘无故地重启?是否无缘无故发生“HardFault”呢?是否试着把栈调大一些,问题就少了,再调大一些,问题就不发生了呢?这些问题都是对栈大小的拿捏问题,即是否配置了合适的栈大小?那么多少算合适呢?有没有量化标准呢?关于RAM分配使用问题可以参考《从事开发多年,分不清STM32的RAM使用情况?一图弄清它 》包含已分配使用部分与剩余多少均有测试方法。
2、量化栈使用率
如何量化栈使用率,我们通常使用百分比来量化某些参数,比如考试100分满分60分及格,这里我们也使用百分比来量化栈使用率。量化公式如下:
栈使用率=100X历史使用栈大小/分配栈大小
注意:下文计算栈使用率时计算单位为字而不是字节(即四字节为一个计量单位)。
特别规定,系统连续运行一定时间(如24小时)后“栈使用率”不超过60%为及格。要注意的是这里我们是以不超60%为及格与考试不一样哟,与考驾照更不一样。要是达到90%及以上就等着系统崩溃吧。
3、堆栈的区别
在大部分时候我们都是将它拿到一起说的称之为“堆栈”以至于分开来说还有些人不明白,事实上堆与栈是两个不同的东西,即堆就是堆,栈就是栈。
堆:想象一下地面上有一堆沙,沙越多就堆得越高,如下图所示:
如上图,堆形象地说就像堆沙堆一样,其地址从低到高向上增长,越堆越高,越拿走越低/少。
栈:其地址是从高到低向下增长的。想象有一条栈道,如下图所示:
把堆与栈合并到一起如下图所示(以一个工程实例分配说明)
上图是我一个工程堆栈分配的地址值,不同项目,或者随着项目功能的变化其值也会变化,或者改变S文件中堆栈大小配置项,其值也会变化。此处仅举例接近实现情况。
补充说明:上面说的向下或向上增长,指的都是地址。
4、统计栈历史使用率(百分比)
在文章《从事开发多年,分不清STM32的RAM使用情况?一图弄清它 》中已经介绍了栈顶地址的获取方法(即读取地址0x08000000处的值),再引用S文件中栈大小的值即可知道栈的大概情况;现在我们的任务是要统计栈使用率,所以首要任务是初始化栈区为某个已知值(比如0);然后从栈底开始读取栈的值,再与初始化值比较,与初始值相等即为尚未使用过,未使用计算器加1,如此往复读取整个栈区的地址空间,直到遇到于初始值不一致时退出读栈过程。这样就得到未使用部分的空间;据此即可计算栈的使用率。如下图所示:
5、测试栈使用率
为了测栈使用率,我定义两个不同函数,其内部定义不同数量的局部变量,调用函数前后统计栈使用率:我在配置栈总大小4096字节,函数Test1()中定义局部变量1000字节(使用率约24%),函数Test1()中定义局部变量3000字节(使用率约73%)。如下图所示:
6、关键测试代码如下
#define APP_START_ADDRESS 0x08000000 //开始地址(栈顶地址)#define RAM_START_ADDRESS 0x20000000 //RAM开始地址#define APP_STACK_SIZE 0x1000 //栈大小(字节数量)4KB#define APP_HEAP_SIZE 0x1000 //堆大小(字节数量)4KB#define MCU_RAM_SIZE 0x5000 //20KB// --------------------------------------------------#define BUFF_SIZE 1024 //定义公共大小 char buff[BUFF_SIZE]; //定义公共变量int GetGloablVarSum(void){ int i,sum=0; //局部变量 for(i=0;i<BUFF_SIZE;i++) //公共变量初始化 { buff[i]=i; sum+=buff[i]; //计算功能变量的和 } return sum; //返回计算和 }// --------------------------------------------------int GetHeapVarSum(void){ int i,sum=0; //定义公共变量 char *p1,*p2=(char*)malloc(BUFF_SIZE); //从堆区申请内存 p1=p2; memcpy(p2,buff,BUFF_SIZE); //将公共变量的值拷贝到申请的内存中 p1=p2; for(i=0;i<BUFF_SIZE;i++) //计算申请内存中值的和 { sum+=*p2++; } return sum; //返回计算和}// --------------------------------------------------void TellAboutRam(){ unsigned int StackTopAddr=(*(__IO u32*)APP_START_ADDRESS); //获取栈顶地址 unsigned int HeapTopAddr=StackTopAddr-APP_STACK_SIZE; //获取堆顶/栈底地址 unsigned int GlobalVarSize=HeapTopAddr-APP_HEAP_SIZE-RAM_START_ADDRESS; //计算公共变量区域大小 unsigned int RamFreeSize=MCU_RAM_SIZE-(StackTopAddr-RAM_START_ADDRESS); //计算空闲区大小 printf ("栈 顶\t=\t%08XH \n",StackTopAddr); printf ("堆 顶\t=\t%08XH \n",HeapTopAddr); printf ("未分配区\t=\t%d Byte\n",RamFreeSize); printf ("栈 区\t=\t%d Byte\n",APP_STACK_SIZE); printf ("堆 区\t=\t%d Byte\n",APP_HEAP_SIZE); printf ("公共变量区\t=\t%0d Byte\n",GlobalVarSize);}// --------------------------------------------------u8 GetMcuStackUsePercent(u8 temp){ u8 StackUsePercent=0; //栈使用百分比 u32 i; u32 StackTopAddr=0; //栈顶地址 u32 StackAddrNow=0; //栈当前地址(读值时用) u32 StackTotalWordSize=0; //栈的字大小 u32 StackData=0; //栈中的数据 u32 StackUnuseWordSize=0; //栈中未使用字数量统计 StackTotalWordSize=APP_STACK_SIZE/4; //计算栈控制字数量 StackTopAddr=(*(__IO u32*)APP_START_ADDRESS); //读取栈顶地址 StackAddrNow=StackTopAddr-APP_STACK_SIZE; //计算栈底地址 for(i=0;i<StackTotalWordSize;i++) { StackData=(*(__IO u32*)StackAddrNow); //读取栈地址处的值 StackAddrNow+=4; //地址往栈顶增加 if(StackData==0) //将栈数据与初始值比较 { StackUnuseWordSize++; //栈数据==初始值 } else { break; //栈数据不等于初始值,退出统计过程 } } //计算栈使用率 StackUsePercent=100*(StackTotalWordSize-StackUnuseWordSize)/StackTotalWordSize; #if 0 printf(" ------------- %d -------------\r\n",temp); printf(" 1 APP_START_ADDRESS=%08XH \r\n",APP_START_ADDRESS); printf(" 2 Stack Addr Top=%08XH Buttom=%08XH\r\n",StackTopAddr,StackTopAddr-APP_STACK_SIZE); printf(" 3 STACK_SIZE=%04XH %d Bytes\r\n",APP_STACK_SIZE,APP_STACK_SIZE); printf(" 4 StackSize Total=%d Unuse=%d Used=%d\r\n",StackTotalWordSize,StackUnuseWordSize,StackTotalWordSize-StackUnuseWordSize); #endif return StackUsePercent; //返回栈使用率}void Test1(void){ char temp[1000]; //局部变量(1000/4096=0.244=24%) int i,sum=0; //局部变量 for(i=0;i<1000;i++) { temp[i]=i; sum+=temp[i]; //计算功能变量的和 }}void Test2(void){ char temp[3000]; //局部变量(3000/4096=0.732=73.2%) int i,sum=0; //局部变量 for(i=0;i<3000;i++) { temp[i]=i; sum+=temp[i]; //计算功能变量的和 }}int main (void) { SER_Init (); printf ("------------- Hello World ------------\n"); printf ("测试公共变量\t=\t%d \t\n",GetGloablVarSum()); printf ("测 堆区\t=\t%d \t\n",GetHeapVarSum()); TellAboutRam(); printf(" Test1前 栈使用率=%d%%\r\n",GetMcuStackUsePercent(1)); Test1(); printf(" Test1后 栈使用率=%d%%\r\n",GetMcuStackUsePercent(1)); Test2(); printf(" Test2后 栈使用率=%d%%\r\n",GetMcuStackUsePercent(1)); while (1) { DelayMs(1000); } }7、仿真测试
测试结果如下图
8、总结
统计栈使用率很简单,关键是需要得到栈顶地址与栈大小。还有其他方法欢迎留言交流。喜欢我的文章请关注收藏。
标签: #堆栈使用率行业标准