Linux 应用程序在被内核调入内存中运行后就成为一个进程,因此分析应用程序的地址空间实际上就是分析进程的地址空间分布。
应用程序的地址空间实际上由以下几个部分组成:代码段、初始化数据段、未初始化数据段(bss段)、堆、栈。其在内存中的分布如下:
APUE给出了各个段所包含内容的详细介绍,这里笔者用一个比较直观的方法验证各个段所包含的内容。
方法的思路是:编写一个含有各种数据的C程序,程序中打印出各个变量的地址,编译好以后让其在Linux中运行起来,接着通过查看进程的各个段的信息,进而得到,把打印出来的变量地址与进程各个段的地址区间比较,可以得出变量处在哪个段中。
下面是测试程序:
#include <stdio.h>
#include <malloc.h>
int global_init_a = 1; //全局、初始化
int global_uninit_a; //全局、未初始化
static int global_static_init_a = 2; //全局、静态、初始化
static int global_static_uninit_a; //全局、静态、未初始化
const global_const_a = 3; //全局、只读变量
int main(int argc, char *argv[])
{
int local_init_a = 4; //局部、初始化
int local_uninit_a; //局部、未初始化
static int local_static_init_a = 5; //局部、静态、初始化
static int local_static_uninit_a; //局部、静态、未初始化
const local_const_a = 3; //局部、常量
int *p;
p = (int*)malloc(sizeof(int)); //动态分配
printf("&global_init_a = %p,global_init_a = %d\n",&global_init_a, global_init_a);
printf("&global_uninit_a = %p,global_uninit_a = %d\n",&global_uninit_a, global_uninit_a);
printf("&global_static_init_a = %p,global_static_init_a = %d\n",&global_static_init_a, global_static_init_a);
printf("&global_static_uninit_a = %p,global_static_uninit_a = %d\n",&global_static_uninit_a, global_static_uninit_a);
printf("&global_const_a = %p,global_const_a = %d\n",&global_const_a, global_const_a);
printf("&local_init_a = %p,localinit_a = %d\n",&global_init_a, global_init_a);
printf("&local_uninit_a = %p,local_uninit_a = %d\n",&local_uninit_a, local_uninit_a);
printf("&local_static_init_a = %p,local_static_init_a = %d\n",&local_static_init_a, local_static_init_a);
printf("&local_static_uninit_a = %p,local_static_uninit_a = %d\n",&local_static_uninit_a, local_static_uninit_a);
printf("&local_const_a = %p,local_const_a = %d\n",&local_const_a, local_const_a);
printf("p = %p\n",p);
while(1);
return 0;
}
将代码运行起来,得到如下信息:
图 1
另开一个终端,输入命令: ps aux 、cat /proc/17709/maps 查看进程信息如下:
图 2
从图 2 中可以看出各个段的区间为:
代码段:0x08048000 -- 0x08049000
数据段:0x08049000 -- 0x0804a000
堆(heap):0x094f3000 -- 0x09514000
栈(stack):0xbfd8f000 -- 0xbfda4000
接着通过查看图 1中的各个变量的地址,可以看出其落在哪个区间,进而得出其实际上是处在哪个段。对比的过程省略,下面给出总结后的规律:
1.代码段:代码,全局常量(const)、字符串常量2.数据段:全局变量(初始化以及未初始化的)、静态
变量(全局的和局部的、初始化的以及未初始化的)
3.堆:动态分配的区域
4.栈:局部变量(初始化以及未初始化的,但不包含静
态变量)、局部只读变量(const)
这里还没有分析出bss段中的数据。可以使用命令: readelf -S app_maps_test 得出如下:
找到 .bss段,再将变量的地址带入,就可以看出哪种变量是存放在bss段的。