程序中的数据存储剖析
2010年08月13日
未初始化的全局变量(.bss段)
已经记不清bss代表Block Storage Start还是Block Started by Symbol。像我这种没有和那些古董级计算机打过交道的人,终究无法理解这样怪异的名字,记不住也就不足为奇了。不过没有关系,我们不必纠结于bss究竟代表什么,而是要弄清楚bss段中都会存放些什么数据、这些数据都有什么样的特点以及我们该如何使用它们。
通俗地讲,bss段被用来存放那些没有初始化或初始化为0的全局变量。它有什么特点呢,让我们先来看看一个小程序的表现。
int bss_array[1024 * 1024];
int main(int argc, char* argv[])
{
return 0;
}
# gcc -g bss.c -o bss.exe
# ls -l bss.exe
-rwxrwxr-x 1 root root 5975 Nov 16 09:32 bss.exe
# objdump -h bss.exe
grep bss
24 .bss 00400020 080495e0 080495e0 000005e0 2**5
变量bss_array的大小为4M,而可执行文件的大小只有5K。由此可见,bss类型的全局变量只占运行时的内存空间,而不占用文件空间。
现在大多数操作系统在加载程序时,会把所有的bss全局变量清零。但为了保证程序的可移植性,最好能手工把这些变量初始化为0,这样可以使这些变量都有个确定的初始值。
当然了,作为全局变量,在整个程序的运行周期内,bss数据是一直存在的。
初始化过的全局变量(.data段)
与bss相比,data段就容易理解多了,看名称就大概能知道它里面存放着数据。当然,如果数据全是0,为了优化考虑,编译器会把它当作bss处理。通俗地讲,data段被用来存放那些初始化为非0值的全局变量。那么它又有什么特点呢,我们还是先来看看一个小程序的表现。
int data_array[1024 * 1024] = {1};
int main(int argc, char* argv[])
{
return 0;
}
# ls -l data.exe
-rwxrwxr-x 1 root root 4200313 Nov 16 09:34 data.exe
# objdump -h data.exe
grep \\.data
23 .data 00400020 080495e0 080495e0 000005e0 2**5
仅仅是把初始化的值改为非0值了,文件就变为4M多。由此可见,data类型的全局变量是既占文件空间,又占用运行时内存空间的。
同样,作为全局变量,在整个程序的运行周期内,data数据也是一直存在的。
常量数据(.rodata段)
rodata的意义同样明显,ro代表read only(只读),rodata就是用来存放常量数据的。关于rodata类型的数据,要注意以下几点。
(1) 常量不一定就放在rodata里,有的立即数直接和指令编码在一起,存放在代码段(.text)中。
(2) 对于字符串常量,编译器会自动去掉重复的字符串,保证一个字符串在一个可执行文件(EXE/SO)中只存在一个副本。
(3) rodata是在多个进程间共享的,这样可以提高运行空间利用率。
(4) 在有的嵌入式系统中,rodata放在ROM(或者NOR闪存芯片)里,运行时直接读取,无需加载到RAM中。
(5) 在嵌入式Linux系统中,也可以通过一种叫作XIP(就地执行)的技术直接读取常量数据,而无需加载到RAM中。
(6) 常量是不能修改的,修改常量在Linux下会出现段错误。
由此可见,把在运行过程中不会改变的数据设为rodata类型是有好处的。在多个进程间共享,可以大大提高空间利用率,甚至能不占用RAM空间。同时由于rodata在只读的内存页面中是受保护的,任何试图对它进行修改的行为都会被及时发现,这样一来还可以提高程序的稳定性。
字符串会被编译器自动放到rodata中,其他数据要放到rodata中,只需要为其加const关键字修饰即可。
代码(.text段)
text段存放代码(如函数)和部分整数常量,它与rodata段很相似,相同的特性我们就不重复了,主要的区别在于text段是可以执行的。
栈(stack)
栈是用来存放临时变量和函数参数的。将栈作为一种基本数据结构,我并不感到惊讶;将其用来实现函数调用,也是大家司空见惯的作法。直到我试图找到另外一种方式实现递归操作时,我才感叹于栈的巧妙。要实现递归操作,不用栈不是不可能,只是找不出比使用栈更优雅的方式。
尽管大多数编译器在优化时会把常用的参数或局部变量放入寄存器中,但用栈来管理函数调用时的临时变量(局部变量和参数)才是通行的做法,前者只是辅助手段而已,而且只可以在当前函数中将参数和局部变量存入寄存器,一旦调用下一层函数,这些值还是得存入栈中才行。
通常情况下,栈是向下(低地址)增长的,每向栈中PUSH一个元素,栈顶就向低地址扩展,每从栈中POP一个元素,栈顶就向高地址回退。这里有一些比较有意思的问题:在x86平台上,栈顶寄存器为ESP,那么ESP的值是在PUSH操作之前修改呢,还是在PUSH操作之后修改呢?PUSH ESP这条指令会向栈中存入什么数据呢?据说x86系列CPU中,除了286外,都是先修改ESP,再压栈的。由于286没有CPUID指令,因此有的操作系统会用这种方法检查286的型号。
要注意的是,存放在栈中的数据只在当前函数及下一层函数中有效,一旦函数返回了,这些数据也就自动释放了,继续访问这些变量会造成意想不到的错误。
堆(heap)
堆是最灵活的一种内存,它的生命周期完全由使用者控制。标准C提供以下几个函数来使用堆内存。
函数名 用途
Malloc 用来分配一块指定大小的内存。
Realloc 用来调整/重分配一块存在的内存。
free 用来释放不再使用的内存。
使用堆内存时请注意以下两个问题。
1, alloc/free要配对使用。我们将内存分配了而不释放的情形称为内存泄露(memory leak),如果内存泄露的情况过多出现,迟早会造成"Out of memory"(内存不足)的错误,从而无法再成功分配内存。当然释放时也只能释放已经被分配的内存,释放无效的内存或重复释放都是不行的,会造成程序崩溃。
2, 分配多少用多少。分配了100字节就只能用100字节,不管是读还是写,都只能在这个范围内,读多了会读到随机的数据,写多了会造成随机的破坏。我们将读写分配范围外的数据的情况称为缓冲区溢出(buffer overflow),这种情况是非常严重的,大部分安全问题都是由缓冲区溢出引起的。
想手工检查有无内存泄露或缓冲区溢出是很困难的,幸好有些工具可供使用,比如Linux下有valgrind,它的使用方法很简单,大家下去可以试用一下,以后每次写完程序都应该用valgrind跑一遍。
最后,我们来看看在Linux下,程序运行时空间的分配情况。
# cat /proc/self/maps
00110000-00111000 r-xp 00110000 00:00 0 [vdso]
009ba000-009d6000 r-xp 00000000 08:01 768759 /lib/ld-2.8.so
009d6000-009d7000 r--p 0001c000 08:01 768759 /lib/ld-2.8.so
009d7000-009d8000 rw-p 0001d000 08:01 768759 /lib/ld-2.8.so
009da000-00b3d000 r-xp 00000000 08:01 768760 /lib/libc-2.8.so
00b3d000-00b3f000 r--p 00163000 08:01 768760 /lib/libc-2.8.so
00b3f000-00b40000 rw-p 00165000 08:01 768760 /lib/libc-2.8.so
00b40000-00b43000 rw-p 00b40000 00:00 0
08048000-08050000 r-xp 00000000 08:01 993652 /bin/cat
08050000-08051000 rw-p 00007000 08:01 993652 /bin/cat
0805f000-08080000 rw-p 0805f000 00:00 0 [heap]
b7fe8000-b7fea000 rw-p b7fe8000 00:00 0
bfee7000-bfefc000 rw-p bffeb000 00:00 0 [stack]
每个区间都有四个属性:
r 表示可以读取;
w 表示可以修改;
x 表示可以执行;
p/s 表示是否为共享内存。
对有文件名的内存区间而言:
属性为r--p表示存放的是rodata;
属性为rw-p表示存放的是bss和data;
属性为r-xp表示存放的是text数据。
没有文件名的内存区间则表示用mmap映射的匿名空间。
文件名为[stack]的内存区间表示是栈。
文件名为[heap]的内存区间表示是堆。
对内存的掌握是系统程序员必备的技能,希望大家多加体会。
发表评论
-
linux性能调优命令精华
2012-01-20 08:30 584linux性能调优命令精华 ... -
多线程服务器的常用编程模型
2012-01-20 08:29 635多线程服务器的常用编 ... -
qt线程(转)----这篇很专业!(五至九 部分)
2012-01-20 08:23 1585qt线程(转)----这篇很专 ... -
linux内核空间与用户空间信息交互方法
2012-01-20 08:23 851linux内核空间与用户空 ... -
VBS脚本
2012-01-19 13:36 562VBS脚本 2011年06月30日 我用VBS写的往EX ... -
xp、2003开3389+非net创建管理用户+Shift后门+自删除脚本+提权VBS 整理收集
2012-01-19 13:35 696xp、2003开3389+非net创建管理用户+Shift后门 ... -
Trojan.DL.VBS.Agent.r 脚本病毒 ASP解密
2012-01-19 13:35 714Trojan.DL.VBS.Agent.r 脚本病毒 ASP解 ... -
【黑客】利用VBS脚本让QQ永远在线,等级速升
2012-01-19 13:35 901【黑客】利用VBS脚本让QQ永远在线,等级速升 2010年0 ... -
用vbs实现获取电脑硬件信息的脚本-1
2012-01-19 13:35 2258用vbs实现获取电脑硬件信息的脚本-1 2011年11月19 ... -
vista 系统问题
2012-01-17 03:25 620vista 系统问题 2010年06月04日 Vista ... -
【精】【爆】MTK手机安装软件游戏大全!新人必看
2012-01-17 03:25 1221【精】【爆】MTK手机安装软件游戏大全!新人必看 2011年 ... -
WP7
2012-01-17 03:25 640WP7 2011年08月07日 Zune应用程序,即“Z ... -
java开发环境搭建
2012-01-17 03:25 619java开发环境搭建 2011年 ... -
从墓葬看契丹文化
2012-01-16 02:00 702从墓葬看契丹文化 2009 ... -
中国人种和起源之谜
2012-01-16 02:00 1174中国人种和起源之谜 2009年11月06日 由于最近一直 ... -
哪里的女人最漂亮
2012-01-16 02:00 625哪里的女人最漂亮 2010 ... -
acegi过滤器介绍-未觉池塘青草梦,阶前梧叶已秋声-iteye技术网站
2012-01-11 02:02 624acegi过滤器介绍-未觉池 ... -
spring + hibernate + struts2 + compass2.1--iteye技术网站
2012-01-11 02:02 538spring + hibernate + struts2 + ... -
servlet 学习笔记-hekeji-iteye技术网站
2012-01-11 02:02 602servlet 学习笔记-hekeji-ite ... -
css 表格 table
2012-01-11 02:02 677css 表格 table 2011年08月01日 css ...
相关推荐
Android手机App程序中SQLite数据存储应用.pdf
可以数据“342”存入ha.txt文件中的程序。 运行这个程序可以把数据存入“342 ”存入ha.txt文件中...此程序只是个示例,如果读者剖析了此程序,就会知道如何编写这么一种程序,运行它就可以把一个数据存入指定的文件中。
唐诗分析程序主要是通过抓取互联网上的唐诗,然后进行数据的清洗,存储,数据分析,输出报告。 2. 背景 随着Java的发展,提供了流式处理(Stream)数据的能力,以及数据挖掘也是近年来比较热门的技术职业发展方向。...
C++基于阅读数据的智能分析阅读器服务器程序源码。本软件是基于视线追踪技术的智能阅读器服务器程序,基于BRPC框架搭建网络服务,可实现跨平台网络交互,根据用户阅读时的视线数据分析用户阅读状态,提供个性化学习...
微信小程序数据分析教程 功能概述 小程序数据分析,是面向小程序开发者、运营者的数据分析工具,提供关键指标统计、 实时访问监控、自定义分析等,帮助小程序产品迭代优化和运营。主要功能如下: 概况:提供小程序...
程序存储问题 设有n个程序{1,2,…, n }要存放在长度为L的磁带上。程序i存放在磁带上的长度是li,1。程序存储问题要求确定这n个程序在磁带上的一个存储方案,使得能够在磁带上存储尽可能多的程序。 Input 测试数据...
第8章综合介绍操作系统和编译程序中涉及的动态存储管理的基本技术;第9章至第11章讨论查找和排序,除了介绍各种实现方法之外,并着重从时间上进行定性或定量的分析和比较;第12章介绍常用的文件结构。 《数据结构》...
分析从VIVADO存储下来的ILA数据,解析出AD各个通道数据并分析相位差
3.5.4 字符数据在内存中的存储形式及使用方法 41 3.5.5 字符串常量 41 3.5.6 符号常量 42 3.6 变量赋初值 42 3.7 各类数值型数据之间的混合运算 43 3.8 算术运算符和算术表达式 44 3.8.1 C运算符简介 44 3.8.2 算术...
C语言程序设计案例——其中包括学生成绩管理、存储管理分区分配算法、 数据结构CAI系统 等问题
解析单片机程序存储和数据存储的相关区别,有助于理解单片机的数据存储方式
必须提交的材料 • 下载的数据集:各个数据集各自存入一个文件中,文件名为程序中使用该数据集时的名称; • python 的源程序:每个源程序存入一个文件,文件名能体现其作用; • pdf 版本的技术报告; • 以上三...
知识点:数据存储技术 文件形式存储 TXT格式存储 优点:代码简单。 缺点:后期处理分析都不方便。 文件形式存储 CSV格式存储 逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值),其文件以纯文本形式...
3.5.4 字符数据在内存中的存储形式及使用方法 41 3.5.5 字符串常量 41 3.5.6 符号常量 42 3.6 变量赋初值 42 3.7 各类数值型数据之间的混合运算 43 3.8 算术运算符和算术表达式 44 3.8.1 C运算符简介 44 3.8.2 算术...
二叉树各基本操作时间复杂度与存储结构特点分析及对算法的改进设想。 六.实验总结和体会 实现的基本操作如下: InitBiTree(&T) DestroyBiTree(&T) CreateBiTree(&T) ClearBiTree(&T) BiTreeEmpty(T) BiTreeDepth(T)...
WDF 设备驱动程序的开发技术进行了分析和论述,深入剖析了 WDF 驱动程序模型的基本框架和运行机理,从驱动程序的初始化、IRP 的处理、中断响应、DMA 操作以及应用程序接口等方面详细讨论了高速数据传输卡驱动程序的...
scrapy抓取数据存储至本地mysql数据库 基于python开发,采用scrapy,数据存储至本地数据库(或excel表格) 程序的主要目的是完成抓取和分析的任务同时学习爬虫相关知识,所以在细节处理上略有不足,但考虑到最终的...
本文采取面对对象的开发模式进行软件的开发和硬体的架设,能很好的满足实际使用的需求,完善了对应的软体架设以及程序编码的工作,采取MySQL作为后台数据的主要存储单元,采用JavaEE框架、JSP技术、Ajax技术进行业务...
数据存储: 爬虫将提取的数据存储到数据库、文件或其他存储介质中,以备后续分析或展示。常用的存储形式包括关系型数据库、NoSQL数据库、JSON文件等。 遵守规则: 为避免对网站造成过大负担或触发反爬虫机制,爬虫...
★问题描述:设有n个程序{1,2,…, n }要存放在长度为L的磁带上。程序i存放在磁带上的长度是li,1 £ i £ n。 程序存储问题要求确定这n个程序在磁带...★数据输出:将计算的最多可以存储的程序数输出到文件output.txt。