进击吧,linux(一)-Linux应用程序地址分布
0赞对于一个linux的应用程序,在内存中是分成好几个部分的。每一个部分有自己的作用。
分成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循环是让程序一直执行。这样,我们可以去看到程序在内存中的分布。
程序运行的结果。
使用ps aux查看进程的PID。
得到我们写的程序运行的PID是18456。。
在/proc/18456目录下,保存了该进程的相关信息。查看maps文件,里面记录了该进程的内存分布。
关注图中表示的部分:
段 |
起始地址 |
结束地址 |
属性 |
代码段 |
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段是在数据段中。
使用readelf查看gcc编译生成的elf文件。
看到BSS段的起始地址是0x080499e0。在用这个地址大小去比较前面得到的各个变量。
就可以得到:
在数据段中,只要是没有初始化的变量,都是处于在BSS段中的。
通过以上分析,就可以知道linux应用程序在内存中的地址分布,特别是各个变量,是应该处于什么区域了。