weiqi7777

进击吧,linux(一)-Linux应用程序地址分布

0
阅读(2107)

 

           对于一个linux的应用程序,在内存中是分成好几个部分的。每一个部分有自己的作用。

      clip_image002

      分成5个段:

1、  代码段:用来保存应用程序的代码,及全局的常量以及字符串常量。代码段的首地址都是从0x08048000开始。这个地址是虚拟地址,程序运行的时候,操作系统会将该地址会被转化为物理地址使用。

2、  数据段:用来保存全局的已经初始化的变量,以及局部变量中用static修饰的静态变量。

3、  BSS段:用来保存全局的未初始化的变量。操作系统会自动将这些未初始化的全局变量给清零。

4、  堆:用来动态分配内存空间

5、  栈:程序运行过程中使用,以及保存局部变量。

下面就来写个简单的代码,来看一下应用程序的地址分布。

#include<stdio.h>

#include<stdlib.h>

 
int glabal_init_a = 1;    //全局初始化变量

int glabal_uninit_a;      //全局未初始化变量


static int static_global_init_a = 1;  //全局静态初始化变量

static int static_glabal_uninit_a;    //全局静态未初始化变量


const int const_global_a = 1;  //全局只读变量

 
int main()

{

    int local_init_a=1;     //局部初始化变量

    int local_uninit_a;     //局部未初始化变量
 

    static int static_local_init_a = 1;  //局部静态初始化变量

    static int static_local_uninit_a;    //局部静态未初始化变量


    const int const_local_a = 1; //局部只读变量

    
    int *p;     //局部指针

    p = (int *)malloc(16);

    
    printf("&glabal_init_a = %x, glabal_init_a = %d\n",&glabal_init_a,glabal_init_a);

    printf("&glabal_uninit_a = %x, glabal_uninit_a = %d\n",&glabal_uninit_a,glabal_uninit_a);

    printf("&static_global_init = %x, static_global_init = %d\n",&static_global_init_a,static_global_init_a);

    printf("&static_glabal_uninit_a = %x, static_glabal_uninit_a = %d\n",&static_glabal_uninit_a,static_glabal_uninit_a);

    printf("&const_global_a = %x, const_global_a = %d\n",&const_global_a,const_global_a);

        
    printf("&local_init_a = %x, local_init_a = %d\n",&local_init_a,local_init_a);

    printf("&local_uninit_a = %x, local_uninit_a = %d\n",&local_uninit_a,local_uninit_a);

    printf("&static_local_init_a = %x, static_local_init_a = %d\n",&static_local_init_a,static_local_init_a);

    printf("&static_local_uninit_a = %x, static_local_uninit_a = %d\n",&static_local_uninit_a,static_local_uninit_a);

    printf("&const_local_a = %x, const_local_a = %d\n",&const_local_a,const_local_a);

    printf("p = %x, *p = %d\n",p,*p);

    
    while(1);

    return 0;

}



代码也是比较简单的,就定义了一些变量。然后打印这些变量的地址和数据。最后一个while循环是让程序一直执行。这样,我们可以去看到程序在内存中的分布。

clip_image004

程序运行的结果。

使用ps aux查看进程的PID

clip_image006

得到我们写的程序运行的PID18456。。

/proc/18456目录下,保存了该进程的相关信息。查看maps文件,里面记录了该进程的内存分布。

clip_image008

关注图中表示的部分:

起始地址

结束地址

属性

代码段

0x08048000

0x08049000

只读,可执行

数据段

0x08049000

0x0804a000

可读可写,不可执行

0x08f8c000

0x08fad000

可读可写,不可执行

0xbfe2d000

0xbfe42000

可读可写,不可执行

有了上图的表,在加上程序的结果,就可以分析了。首先是代码段的起始地址是0x08048000,这个值是固定的。

然后是各个变量的位置:

变量

地址

处于位置

全局变量glabal_init_a

0x080499d4

数据段

全局变量glabal_uninit_a

0x080499f0

数据段

全局静态变量

static_global_init_a

0x080499d8

数据段

全局静态变量

static_global_uninit_a

0x080499e8

数据段

全局常量

const_global_a

0x08048634

代码段

局部变量local_init_a

0xbfe40f48

局部变量local_uninit_a

0xbfe40f44

局部静态变量

static_local_init_a

0x080499dc

数据段

局部静态变量

static_local_uninit_a

0x080499ec

数据段

局部常量

const_local_a

0xbfe40f40

局部指针p

0x08f8c0008

           这下,结果就很明了了。

1、  对于全局变量,不管是有无初始化,都是保存在数据区的。对于静态static修饰的变量,无论是在全局变量还是局部变量,也是都保存在数据区了。而全局的常量也是保存在数据区的。

2、  对于局部变量,除了static修饰的变量,都是保存在栈中的。

3、  对于指针,使用malloc分配的,保存在堆中的。


还是一个BSS段。其实BSS段是在数据段中。

clip_image010

使用readelf查看gcc编译生成的elf文件。

看到BSS段的起始地址是0x080499e0。在用这个地址大小去比较前面得到的各个变量。

就可以得到:

     在数据段中,只要是没有初始化的变量,都是处于在BSS段中的。

     通过以上分析,就可以知道linux应用程序在内存中的地址分布,特别是各个变量,是应该处于什么区域了。