物理内存和虚拟内存从硬件上讲,虚拟空间是CPU内部的寻址空间,位于MMU转换之前;物理空间是总线上的寻址空间,是经过MMU转换之后的空间。 例如:对于一台内存为256M的32bit x86主机来说,它的虚拟地址空间范围是0~0xFFFFFFFF(4G),而物理地址空间范围是0x00000000~0x0FFFFFFF(256M)。 一般我们所说的程序在内存中的分布指的就是程序在虚拟内存中的存储方式。 ELF文件可执行文件、可重定位文件(.o)、共享目标文件(.so)、核心转储文件都是以elf文件格式存储的。 Linux可执行文件格式为ELF即Executable and Linkable Format。而a.out 是可执行文件也属于ELF文件。 ELF文件组成部分:文件头、段表(section)、程序头;其中我们要关注的是段表中的几个重要的段:.text(代码)段 、.data(数据)段、.bss段 。
下图section表示相同或者相似信息的集合,.text(代码)段 、.data(数据)段、.bss段分别对应各自的section 。 实际上我们也可以将一个程序的所有内容都放在一起,就像dos一样,但是将可执行程序分成多个section是很有好处的,比如说我们可以将.text section放在memory的只读空间内,将可变的.data section放在memory的可写空间内。 命令 $ readelf -l a.out 可查看文件的格式和组成。
构成及内存分配汇编语言及高级语言程序执行时内存分配:
1.一般情况下,一个可执行二进制程序(更确切的说,在Linux操作系统下为一个进程单元,在UC/OSII中被称为任务)在存储(没有调入到内存运行)时拥有3个部分,分别是代码段(text)、数据段(data)和BSS段。这3个部分一起组成了该可执行程序的文件。 ★★可执行二进制程序 = 代码段(text)+数据段(data)+BSS段★★ 2.而当程序被加载到内存单元时,则需要另外两个域:堆域和栈域。图1-1所示为可执行代码存储态和运行态的结构对照图。一个正在运行的C程序占用的内存区域分为代码段、初始化数据段、未初始化数据段(BSS)、堆、栈5个部分。 ★★正在运行的C程序 = 代码段+初始化数据段(data)+未初始化数据段(BSS)+堆+栈★★ 3.在将应用程序加载到内存空间执行时,操作系统负责代码段、数据段和BSS段的加载,并将在内存中为这些段分配空间。栈亦由操作系统分配和管理,而不需要程序员显示地管理;堆段由程序员自己管理,即显示地申请和释放空间。 4.动态分配与静态分配,二者最大的区别在于:1. 直到Run-Time的时候,执行动态分配,而在compile-time的时候,就已经决定好了分配多少Text+Data+BSS+Stack。2.通过malloc()动态分配的内存,需要程序员手工调用free()释放内存,否则容易导致内存泄露,而静态分配的内存则在进程执行结束后系统释放(Text, Data), 但Stack段中的数据很短暂,函数退出立即被销毁。 注:从可执行文件a.out的角度来讲,如果一个数据未被初始化那就不需要为其分配空间,所以.data和.bss一个重要的区别就是.bss并不占用可执行文件的大小,它只是记载需要多少空间来存储这些未初始化数据,而不分配实际的空间。故对于可执行文件大小来说,.bass几乎是不占用空间的。
各段说明
代码段 --text(code segment/text segment) text段在内存中被映射为只读,但.data和.bss是可写的。 数据段 -- datadata包含静态初始化的数据,所以有初值的全局变量和static变量在data区。段的起始位置也是由连接定位文件所确定,大小在编译连接时自动分配,它和你的程序大小没有关系,但和程序使用到的全局变量,常量数量相关。数据段属于静态内存分配。 bss段--bssbss是英文Block Started by Symbol的简称,通常是指用来存放程序中未初始化的全局变量的一块内存区域,在程序载入时由内核清0。BSS段属于静态内存分配。它的初始值也是由用户自己定义的连接定位文件所确定,用户应该将它定义在可读写的RAM区内,源程序中使用malloc分配的内存就是这一块,它不是根据data大小确定,主要由程序中同时分配内存最大值所确定,不过如果超出了范围,也就是分配失败,可以等空间释放之后再分配。BSS段属于静态内存分配。 stack:栈(stack)保存函数的局部变量(但不包括static声明的变量, static 意味着 在数据段中 存放变量),参数以及返回值。是一种“后进先出”(Last In First Out,LIFO)的数据结构,这意味着最后放到栈上的数据,将会是第一个从栈上移走的数据。对于哪些暂时存贮的信息,和不需要长时间保存的信息来说,LIFO这种数据结构非常理想。在调用函数或过程后,系统通常会清除栈上保存的局部变量、函数调用信息及其它的信息。栈另外一个重要的特征是,它的地址空间“向下减少”,即当栈上保存的数据越多,栈的地址就越低。栈(stack)的顶部在可读写的RAM区的最后。 heap:堆(heap)保存函数内部动态分配内存,是另外一种用来保存程序信息的数据结构,更准确的说是保存程序的动态变量。堆是“先进先出”(First In first Out,FIFO)数据结构。它只允许在堆的一端插入数据,在另一端移走数据。堆的地址空间“向上增加”,即当堆上保存的数据越多,堆的地址就越高。
举例说明:源码:1 #include <stdio.h> 2 const int g_A = 10; //只读数据段 3 int g_B = 20; //数据段 4 static int g_C = 30; //数据段 5 static int g_D; //BSS段 6 int g_E; //BSS段 7 char *p1; //BSS段 8 9 void main( ) 10 { 11 int local_A; //栈 12 int local_B; //栈 13 static int local_C = 0; //数据段 14 static int local_D; //数据段 15 16 char *p3 = "123456"; //123456在代码段,p3在栈上 17 18 p1 = (char *)malloc( 10 ); //堆,分配得来得10字节的区域在堆区 19 strcpy( p1, "123456" ); //123456{post.content}放在常量区,编译器可能会将它与p3所指向 的"123456"优化成一块 20 21 printf("hight address\n"); 22 printf("-------------栈--------------\n"); 23 printf( "栈, 局部变量, local_A, addr:0x%08x\n", &local_A ); 24 printf( "栈, 局部变量,(后进栈地址相对local_A低) local_B, addr:0x%08x\n", &local_B ); 25 printf("-------------堆--------------\n"); 26 printf( "堆, malloc分配内存, p1, addr:0x%08x\n", p1 ); 27 printf("------------BSS段------------\n"); 28 printf( "BSS段, 全局变量, 未初始化 g_E, addr:0x%08x\n", &g_E, g_E ); 29 printf( "BSS段, 静态全局变量, 未初始化, g_D, addr:0x%08x\n", &g_D ); 30 printf( "BSS段, 静态局部变量, 初始化, local_C, addr:0x%08x\n", &local_C); 31 printf( "BSS段, 静态局部变量, 未初始化, local_D, addr:0x%08x\n", &local_D); 32 printf("-----------数据段------------\n"); 33 printf( "数据段,全局变量, 初始化 g_B, addr:0x%08x\n", &g_B); 34 printf( "数据段,静态全局变量, 初始化, g_C, addr:0x%08x\n", &g_C); 35 printf("-----------代码段------------\n"); 36 printf( "只读数据段,全局初始化变量, 只读const, g_A, addr:0x%08x\n\n", &g_A); 37 printf("low address\n"); 38 return; 39 } 运行结果:1 hight address 2 -------------栈-------------- 3 栈, 局部变量, local_A, addr:0xffa70c1c 4 栈, 局部变量,(后进栈地址相对local_A低) local_B, addr:0xffa70c18 5 -------------堆-------------- 6 堆, malloc分配内存, p1, addr:0x087fe008 7 ------------BSS段------------ 8 BSS段, 全局变量 未初始化 g_E, addr:0x08049a64 9 BSS段, 静态全局变量 未初始化 g_D, addr:0x08049a5c 10 BSS段, 静态局部变量, 初始化, local_C, addr:0x08049a58 11 BSS段, 静态局部变量, 未初始化, local_D, addr:0x08049a54 12 -----------数据段------------ 13 数据段,全局变量, 初始化 g_B, addr:0x08049a44 14 数据段,静态全局变量, 初始化, g_C, addr:0x08049a48 15 -----------只读数据段------------ 16 只读数据段,全局初始化变量, 只读const, g_A, addr:0x08048620 17 18 low address 各段的地址:通过命令查看这个执行文件a.out 的elf信息,摘录部分如下:重点注意其中data段,text段还要有bss段的地址,然后比较这个地址和上面的运行结果,是否是在elf文件的各个段的地址之内。 注意:编译时需要-g选项,这样才可以看elf信息; readelf -a a.out
★★★★注意静态变量初始化为零和全局静态变量初始化为零的情况,都是存储在bss段(比如local_C)★★★★ 从上面的elf文件可以看出各个段的对应地址,符合上述输出结果。
|
|