weiqi7777

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

0
阅读(2044)

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

clip_image002

分成5个段:

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

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

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

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

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

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

#include #include 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应用程序在内存中的地址分布,特别是各个变量,是应该处于什么区域了。

Baidu
map