关于协议栈XDATA,内存溢出的小结
【第二部分的内容仅供参考,自己不是十分确定】
**************************************************************
**************************************************************
**************************************************************
一、
1、单片机的存储器分为数据存储器(RAM)和程序存储器(ROM/FLASH):
RAM:用来存取各种动态的输入输出数据,中间计算结果以及与外部存储器交换的数据和暂存数据。设备掉电后,数据就会丢失。
ROM:通常用来固化存储一些用户写入程序或数据,用于启动设备和控制设备工作方式。设备掉电后可保存数据。
FLASH:通常也是用来固化存储一些用户写入程序或数据。设备掉电后不会丢失数据,同时可以快速读取数据。U盘、MP3多用这种存储器。
RAM包括:
(1)静态RAM(SRAM):速度非常快,是目前读写最快的存储设备。
(2)动态RAM(DRAM):保留数据时间短,但速度比ROM快,计算机内存多为DRAM。DRAM包括:EDORAM;DDR RAM;RDRAM;SGRAM;WRAM…
ROM包括:
(1)PROM:可编程,一次性
(2)EPROM:可擦除可编程,通过紫外线擦除
(3)EEPROM:擦除可编程,通过电子擦除
FLASH包括:(具体参见http://wjf88223.blog.163.com/blog/static/35168001201092685333808/)
(1)NOR FLASH
(2)NAND FLASH
2、存储类型与存储区关系(单片机)
data ---> 可寻址片内ram
bdata ---> 可位寻址的片内ram
idata ---> 可寻址片内ram,允许访问全部内部ram
pdata ---> 分页寻址片外ram (MOVX @R0) (256 BYTE/页)
xdata ---> 可寻址片外ram (64k 地址范围FFFFH)
code ---> 程序存储区 (64k 地址范围),对应MOVC @DPTR
3、一个C语言程序占用的内存分为以下几个部分:
(1)栈区(stack):由编译器自动分配释放,存放函数的参数值,局部变量的值等,其操作方式类似于数据结构中的栈。
(2)堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由操作系统回收。
(3)全局区(静态区):全局变量和静态变量的存储位置是在一起的。初始化的全局变量和静态变量在同一块区 域,而未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,程序结束后由系统自动释放。
(4)文字常量区:这一区域用于存放常量字符串,程序结束后由系统释放。
(5)程序代码区:这一区域用于存放函数体的二进制代码(ROM/FLASH)。
4、堆(heap)和栈(stack)的申请方式
stack:由系统自动分配。比如,声明在函数中的一个局部变量 int b;系统自动在栈中为b开辟空间。
heap:需要程序员自己申请,并指明大小。
在C中malloc函数;如:p1=(char *)mqlloc(10);
在C++中用new运算符;如:p2=new char[20];
注:p1,p2本身是在栈中的。
5、例子:
//main.cpp
int a = 0; //全局初始化区
char *p1; //全局未初始化区
main()
{
int b; //栈
char s[ ] = "abc"; //栈
char *p2; //栈
char *p3 = "123456"; //123456\0在常量区,p3在栈上。
static int c =0; //全局(静态)初始化区
//分配得来得10和20字节的区域就在堆区。
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
}
strcpy(p1, "123456"); //123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
6、变量的存储类型和存储方式
存储类型:
存储方式:
静态存储变量通常是在变量定义时就分配一定的存储空间并一直保持不变,直至整个程序结束。
动态存储变量是在程序执行过程中,使用它时才分配存储单元,使用完毕立即释放。
静态存储变量是一直存在的,而动态存储变量则时而存在时而消失。
全局变量:作用域为整个源程序,即所有源文件;
静态全局变量:作用域为定义该变量的源文件;
把全局变量改变为静态全局变量后改变了它的作用域,而生存期不变;
局部变量:生存期为定义它的函数或复合语句;
静态局部变量:生存期为整个源程序;
把局部变量改变为静态局部变量后改变了它的生存期,而作用域不变;
**************************************************************
**************************************************************
**************************************************************
二、
对《SimpleApp中Controller/Switch绑定小实验》中出现的以下XDATA溢出错误,记录下个人的一点理解.
**************************************************************
《SimpleApp中Controller/Switch绑定小实验》中整个工程编译后的内存占用如下:
【图1】
那在函数zb_ReceiveDataIndication()中添加下面语句:
编译后的内存占用如下:
与图1相比XDATA多了6字节,个人认为这6个字节是:H E L L O \0
buf这个数组应该属于自动变量,属于动态存储方式,只有在使用它,即当buf被调用时才给它分配内存单元,开始它的生存期,调用结束,释放存储单元,结束生存期。
那在zb_ReceiveDataIndication()中这样添加(typedef __code const uint8 CCUINT8;,):
编译后的内存占用如下:
与图1相比可以发现这里字符串并没有占用XDATA,而是占用CODE。
若没有static声明为静态变量,则出现以下错误:
Error[Be009]: memory attributes not allowed on auto variables or parameters
当然,把它声明为全局变量(?常量)也可行,在SimpleController.c开头定义全局变量(?常量):
CCUINT8 buf[ ]="HELLO";
编译后的内存占用如下:
两种方式所占内存一样,全局变量与静态变量的存储方式都是静态存储,存储空间是在编译完成后就分配的,并且在程序运行的全部过程中都不会撤销。
至于出现错误Error[Be009]的原因,和实验室的人讨论也没有得出一个很明确的答案。
typedef __code const uint8 CCUINT8;
CCUINT8 buf[ ]="HELLO";
按理说应该是把HELLO\0放在常量区(???),编译后就分配内存,程序运行中只能读取不能改写。
(1)把CCUINT8 buf[ ]="HELLO";放在SimpleController.c开头声明为全局变量(?常量),这种存储方式应该是符合的;
(2)把static CCUINT8 buf[ ]="HELLO";放在zb_ReceiveDataIndication()中声明为静态局部变量(?常量),这种存储方式也应该是符合的;
(3)把CCUINT8 buf[ ]="HELLO";放在zb_ReceiveDataIndication()中,按我个人理解,编译器会认为它是一个自动变量,因为关键字“auto”可以省略,auto不写则隐含确定为“自动存储类别”,属于动态存储方式,即只有在使用它时才给它分配存储单元,开始它的生存期,使用结束后,释放单元,结束生存期。这种存储方式不符合。
对于typedef __code const uint8 CCUINT8;,我是看到协议栈中hal_adc.c定义过这么个数组:
static __code const uint16 HalAdcVddLimit[] =
{
…………
};
以及在Font.c定义过这么个数组:
__code const INT8U Font8X8[] =
{
………………
}
然后拿过来看看能否解决把字符串定义到CODE中而不占用XDATA,具体很详细的语法意思不知道,总之最终可行。
如果把这个数组定义过大,比如在SimpleController.c开头定义:
CCUINT8 buff[4000];
在zb_ReceiveDataIndication()中:
HalUARTWrite ( 0, (uint8 *)buff, sizeof(buff) );
则编译会出现以下错误:
还缺少0x219字节,即537字节,那我这样定义:CCUINT8 buff[3463];编译没有问题:
那我再加1字节:CCUINT8 buff[3464];出现这错误:
即多了0x01字节。O了~
另一种方法,自己申请一个堆
最初工程内存占用为:
那我在zb_ReceiveDataIndication()中这样添加:
编译后的内存占用如下:
与图1相比发现这样定义不占用XDATA。但个人总感觉如果废话多点,这样把字符一个一个赋进去十分麻烦,可是不知道如何快捷地把一串字符赋给动态开辟的堆……
我这样添加:
与图1相比可以看到编译后的内存占用XDATA还是多了6字节:
我这样添加:
与图1相比可以看到编译后的内存占用XDATA一样还是多了6字节:
……还是不知道怎么赋…= . =''
****************************************
小结[我不敢百分百确定,大家自己可以实验下]:
1、不占用XDATA,对于字符串:
(1) typedef __code const uint8 CCUINT8; 通过CCUINT8定义到CODE中;//char也OK
(2)通过osal_mem_alloc()函数动态开辟堆空间,把字符一个一个赋进去;用完后调用osal_mem_free()进行内存释放!
2、不占用XDATA,对于一堆需要用数组存储的数据:
通过osal_mem_alloc()函数动态开辟堆空间,把数据一个一个赋进去;用完后调用osal_mem_free()进行内存释放!
3、对XDATA溢出,我还没有试验过是否可以通过修改配置文件中的-D_XDATA_END来解决,但看了下貌似不可以,已经达到最大值8K了;
********************
// These settings are used for devices that don't use PM2/PM3
-D_IXDATA_START=E000 // The internal IXDATA block is 8K,
-D_IXDATA_END=FEFF // End of IXDATA if PM2/PM3 are not used
//
// These settings must be used for devices that use PM2/PM3.
// Note that the IXDATA_START allows the XSTACK to grow down into the non-persistent RAM, but
// checks in HAL Sleep insure that the stack is back into persistent RAM before entering PM2/PM3.
//-D_IXDATA_START=EE00 // The internal IXDATA block is 4K+,
//-D_IXDATA_END=FD55 // FD56-FEFF is used for saving the CC2430 registers before sleep.低功耗时这部分用于存储寄存器值
//
// FF00-FFFF is mapped to IDATA.
//
//
// XDATA
//
-D_XDATA_START=_IXDATA_START // The IXDATA is used as XDATA.
-D_XDATA_END=_IXDATA_END
********************
4、若CODE溢出: (1)减小程序;(2)把配置文件f8w2430.xcl/f8w2430pm.xcl中的-D_CODE_END改大点:
********************
f8w2430.xcl:
// CODE
//
// These settings determine the size/location of the ROOT segment.
// Increase _CODE_END to increase ROOT memory, i.e. for constants.
-D_CODE_START=0x0000 // Code size = 128k for CC2430-F128
-D_CODE_END=0x4000 // Last address for ROOT bank
********************
f8w2430pm.xcl:
// CODE
//
// These settings determine the size/location of the ROOT segment.
// Increase _CODE_END to increase ROOT memory, i.e. for constants.
-D_CODE_START=0x0000 // Code size = 128k for CC2430-F128
-D_CODE_END=0x29FF //(原0x28FF) Last address for ROOT bank 这里我修改增大过的。
********************
**************************************************************
**************************************************************
**************************************************************
说明:
1、本文为个人学习笔记,仅供参考.
2、错误之处还请指出,随时更新.
2、欢迎交流,转载请注明出处,谢谢!
更新:2010.12.13 ~XF 2010.12.07
关于协议栈XDATA,内存溢出的小结的更多相关文章
- Android内存溢出解决方案(OOM)
众所周知,每个Android应用程序在运行时都有一定的内存限制,限制大小一般为16MB或24MB(视平台而定).因此在开发应用时需要特别关注自身的内存使用量,而一般最耗内存量的资源,一般是图片.音频文 ...
- 《深入理解Java虚拟机》-----第2章 Java内存区域与内存溢出异常
2.1 概述 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝又是执行最基础工作的劳动人民——拥有每一个对象的“所有权”,又担负着每一个对象生命开始到终结的维护责任 ...
- 【JVM.1】java内存区域与内存溢出
鲁迅曾说过:Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的“高墙”,墙外面的人想进来,墙里面的人想出去. 一.虚拟机内存分布 Java虚拟机在执行Java程序的过程中会把它所管理的内存 ...
- Java之JVM调优案例分析与实战(2) - 集群间同步导致的内存溢出
环境:一个基于B/S的MIS系统,硬件为两台2个CPU.8GB内存的HP小型机,服务器是WebLogic 9.2,每台机器启动了3个WebLogic实例,构成一个6个节点的亲合式集群. 说明:由于是亲 ...
- 关于JVM内存溢出的原因分析及解决方案探讨
前言:JVM中除了程序计数器,其他的区域都有可能会发生内存溢出. 0.什么是内存溢出 当程序需要申请内存的时候,由于没有足够的内存,此时就会抛出OutOfMemoryError,这就是内存溢出. 1. ...
- JMeter内存溢出:java.lang.OutOfMemoryError: Java heap space解决方法
一.问题原因 用JMeter压测,有时候当模拟并发请求较大或者脚本运行时间较长时,JMeter会停止,报OOM(内存溢出)错误. 原因是JMeter是一个纯Java开发的工具,内存由java虚拟机JV ...
- 堆(heap)和栈(stack)、内存泄漏(memory leak)和内存溢出
来源:http://blog.itpub.net/8797129/viewspace-693648/ 简单的可以理解为:heap:是由malloc之类函数分配的空间所在地.地址是由低向高增长的.sta ...
- JVM内存溢出后服务还能运行吗
文章开篇问一个问题吧,一个java程序,如果其中一个线程发生了OOM,那进程中的其他线程还能运行吗? 接下来做实验,看看JVM的六种OOM之后程序还能不能访问. 在这里我用的是一个springboot ...
- Java 内存区域与内存溢出
内存区域 Java 虚拟机在执行 Java 程序的过程中会把他所管理的内存划分为若干个不同的数据区域.Java 虚拟机规范将 JVM 所管理的内存分为以下几个运行时数据区:程序计数器.Java 虚拟机 ...
随机推荐
- ssh-正向与反向代理
常用参数 栗子 实战 常用参数 -N 告诉SSH客户端,这个连接不需要执行任何命令.仅仅做端口转发 -C 表示压缩数据传输 -f 告诉SSH客户端在后台运行 -q Quiet mode. 安静模式,忽 ...
- 面试:Spring面试知识点总结
Spring知识点总结 1. 简介一下Spring框架. 答:Spring框架是一个开源的容器性质的轻量级框架.主要有三大特点:容器.IOC(控制反转).AOP(面向切面编程). 2. Spring框 ...
- Swoole实现毫秒级定时任务
项目开发中,如果有定时任务的业务要求,我们会使用linux的crontab来解决,但是它的最小粒度是分钟级别,如果要求粒度是秒级别的,甚至毫秒级别的,crontab就无法满足,值得庆幸的是swoole ...
- XCTF csaw2013reversing2
题目描述:听说运行就能拿到Flag,不过菜鸡运行的结果不知道为什么是乱码 一.先运行看看. 果然乱码. 二.查壳 三.是pe文件,可以拖入od和ida进行动态和静态分析. 1.对主函数进行反编译一下. ...
- ZYNQ 中PS端GPIO EMIO使用
ZYNQ 中PS端GPIO EMIO使用 在使用ZYNQ进行开发设计时,往往需要对一些GPIO引脚进行配置,传统的配置方法通常在PL端进行管脚约束之后在Verilog代码中对相应引脚进行配置.这样如果 ...
- C语言常用函数笔记
strcmp 比较字符串: sscanf 读取格式化的字符串中的数据: memset 初始化内存的"万能函数",通常为新申请的内存进行初始化工作.对一段内存空间全部设置为某个字符, ...
- 1.前言-聊聊Java这条路
一.解决大家的疑问 1.零基础学习编程? 有编程基础的比零基础的困难,毕竟有一些固定思维 目标:爱好.做网站.做游戏 2.英语不好能学吗? 程序并没有大家想象的那么多英语,天天都在用,慢慢就掌握了 3 ...
- SpringBoot整合Shiro实现权限控制
目录 1.SpringBoot整合Shiro 1.1.shiro简介 1.2.代码的具体实现 1.2.1.Maven的配置 1.2.2.整合需要实现的类 1.2.3.项目结构 1.2.4.ShiroC ...
- 从代码生成说起,带你深入理解 mybatis generator 源码
枯燥的任务 这一切都要从多年前说起. 那时候刚入职一家新公司,项目经理给我分配了一个比较简单的工作,为所有的数据库字段整理一张元数据表. 因为很多接手的项目文档都不全,所以需要统一整理一份基本的字典表 ...
- 【LeetCode】137. 只出现一次的数字 II(剑指offer 56-II)
137. 只出现一次的数字 II(剑指offer 56-II) 知识点:哈希表:位运算 题目描述 给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 .请你找出并返回 ...