关于协议栈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 虚拟机 ...
随机推荐
- 18.自动运维工具ansible
1 Ansible 介绍和架构 1.1 Ansible介绍 ansible 的名称来自科幻小说<安德的游戏>中跨越时空的即时通信工具,使用它可以在相距数光年的 距离,远程实时控制前线的舰队 ...
- 如何消除inline-block元素之间的间隙?
一.问题现象 在CSS布局中,如果我们想要将多个行内块元素并排,会发现同行显示的inline-block元素之间会出现一定的空隙,这就是换行符/空格导致的,叫做换行符/空格间隙. 1 <!DOC ...
- SpringCloud:Eureka注册中心设置显示IP路径
未设置下的Eureka平台 可以看到Status显示的是 计算机名称! 解决方法: 在每一个需要注册的服务配置内加上如下几行配置 instance: prefer-ip-address: true # ...
- 24 shell 管道命令与过滤器
1.管道命令的用法 2.使用管道命令的好处: 3.重定向和管道的区别 4.Linux管道实例 5.管道与重定向 1)管道与输入重定向 2)管道与输出重定向 6.过滤器 7.过滤器举栗 1.管道命令的用 ...
- JavaScript中使用eval()方法解析json串
最近在js用到了eval()方法,在这里做个笔记 当时是这么用的:data = eval("("+data+")"); data为后台向前台传送的一个json串 ...
- JUnit5参数化测试的几种方式
参数化测试一直是津津乐道的话题,我们都知道JMeter有四种参数化方式:用户自定义变量.用户参数.CSV文件.函数助手,那么JUnit5有哪些参数化测试的方式呢? 依赖 JUnit5需要添加junit ...
- python pyinsane应用
import sys from PIL import Image try: import src.abstract as pyinsane except ImportError: import pyi ...
- 传统.NET 4.x应用容器化体验(3)
上一篇我们自己通过编写Dockerfile来编译部署一个ASP.NET MVC应用程序到Windows Container,这一篇我们来试着将.NET 4.x的镜像推送到harbor私有镜像仓库. 1 ...
- linux U盘安装系统工具usb-creator-gtk
linux平台下U盘安装linux系统的工具.它是ubuntu自带的工具,将建时将覆盖U盘中的所有内容.
- 【Mysql】InnoDB 引擎中的页目录
一.页目录和槽 接上一篇,现在知道记录在页中按照主键大小顺序串成了单链表. 那么我使用主键查询的时候,最顺其自然的办法肯定是从第一条记录,也就是 Infrimum 记录开始,一直向后找,只要存在总会找 ...