摘要:本文主要讲述了Linux系统中,程序存储结构(代码区、数据段和BBS区)与进程的基本结构(代码区、数据段、BBS区、堆和栈)
1.Linux程序存储结构
在Linux系统下,程序是一个普通的可执行文件,图1是一个Linux下ELF格式可执行文件的基本情况.图1 ELF格式可执行文件的基本信息
可以看出,此ELF格式可执行文件在存储时,没有调入到内存,分为代码区(text),数据区(data)和为初始化区(bss)3个部分.各段基本说明如下:
(1)代码区(text segment).也称正文段.存放CPU可执行的机器指令,通常代码区是可共享的(即另外的执行代码可以访问调用它)代码区通常是只读的,使其只读的原因是防止程序意外地修改了它的指令.因此,常量数据(const)在编译时在代码段中分配空间.例子1会说明这点.
代码区的指令包括操作码和操作对象(或对象的地址引用),如果是具体数值,将直接包含在代码中.如果是局部数据,将在运行时在栈区分配空间,然后再引用数据的地址;如果是BBS区和数据区,在代码中同样将引用该数据的地址.
(2)全局初始化数据区/静态数据区,或简称数据段.该区包含了在程序中明确被初始化的全局变量、已经初始化的静态变量(包括全局静态变量和局部静态变量).但被const声明的变量以及字符串常量在代码段中申请空间.
(3)未初始化数据区,也称BBS区.存入的是未初始化全局变量和未初始化静态变量.BBS区的数据在程序开始执行之前被内核初始化为0或者空指针(NULL).
例子1:说明常量数据在编译时在代码段中分配空间.
#include <stdio.h>
int main()
{
char *buf =NULL;
printf("%s\n",buf);
return 0;
}
编译后检测各段的大小:
在代码中添加一个字符常量和const数据常量:
#include <stdio.h>
const int i=10;
int main()
{
char *buf = NULL;
printf("%s\n",buf);
return 0;
}
重新编译后查看:代码段的数据增加了4字节的const i.
2.Linux进程结构(地址空间结构)
在Linux系统下,如果将一个ELF格式可执行文件加载到内存中运行,则将演变成一个或多个进程.进程是Linux事务的基本管理单元,所有的进程均拥有自己的独立的环境和资源.进程的环境由当前系统状态及其父进程信息决定和组成.一个正在运行的进程在内存空间中申请的代码区、初始化数据区、未初始化数据区、上下文信息以及挂载的信号等等.
(1)代码区.加载的是可执行文件的代码段,其加载到内存中的位置由加载器完成.
(2)全局初始化数据区/静态数据区.加载的是可执行文件数据段,位置可位于代码段后也可以分开.程序在运行之初就为该数据段申请了空间,在程序退出时才释放,因此,存储于数据段的数据的生存周期为整个程序运行过程.
(3)未初始化数据区.加载的是可执行文件BBS段,位置可以分开也可以紧靠数据段.程序在运行之初为该部分申请了空间,在程序退出时才释放,存储于该部分的数据的生存周期为整个程序运行过程.
(4)栈.由编译器自动分配释放.自动变量以及每次函数调用所需要保存的信息都存放在此段中.每次调用函数时,其返回地址以及调用者的环境信息都存放在栈中.然后,最近被调用的函数在栈上为其自动和临时变量分配存储空间.通过这种方式使用栈,可以递归的调用C函数.递归函数每次调用自身时,就使用一个新的栈帧,因此一个函数调用实例中的变量集不会影响另一个函数调用实例中的变量.
(5)堆,通常在堆中进行动态存储分配.一般由程序员分配和释放,若程序员不释放,程序结束是由系统收回.堆位于非初始化数据段和栈之间.
图2 可执行文件与进程存储布局
3.为什么分这么多个区?
(1)代码段和数据段分开,运行时便于分开加载,在哈佛体系结构的处理器将取得更好的流水线处理效率.(2)代码是依次执行,由处理器的PC指针依次读入,而且代码可以被多个程序共享,数据在整个运行过程中有可能多次被使用,如果将代码和数据混合在一起将造成空间的浪费.
(3)临时数据及需要再次使用的代码在运行时放入栈中,生命周期短,便于提高资源利用率.
(4)堆区可以由程序员分配和释放,以便用户*分配,提高程序的灵活性.
本文转载至http://www.2cto.com/os/201410/341113.html