环境
- 操作系统:ubuntu15.04
- 物理内存:4G
测试程序
#include<stdio.h>
#include<stdlib.h>
int a;
int b=1;
main()
{
int n = 0;
char *p1 = NULL;
char *p2 = NULL;
const int s = 10;
p1 = (char*)malloc(200);
p2 = "hello";
printf("main %p\n",main);
printf("未初始化 a %p\n",&a);
printf("初始化 b %p\n",&b);
printf("局部变量 n %p\n",&n);
printf("动态内存 p1 %p\n",p1);
printf("常量 s %p\n",&s);
printf("常字符串 p2 %p\n",p2);
while(1)
{}
}
查看内存地址空间方式
以下2种方式可以结合着观察
1、 pmap 命令
pmap [参数] [进程pid]
参数:
- -d 显示详细设备信息
- -q 不显示首尾行信息
运行测试程序,用top或ps 命令查询进程pid:
属性 | 含义 |
---|---|
Address | 地址空间:起始地址~ |
Kbytes | 大小 |
Mode | 权限:r可读、w可写、x可执行、s共享内存、p私有内存 |
Offset | 虚拟内存偏移量 |
Device | 所在设备(主:次): 008:00008表示sda8 |
mapped | 虚拟内存分配大小 |
shared | 共享内存大小 |
mem 是运行程序的名字
.so 是使用的动态链接库
stack 使用的栈空间
anon 预分配的虚拟内存,还未有数据占用
2、查看 /proc/pid号/maps 文件内容
这个要比上面显示的更详细一点
每一行依次对应的是:
地址范围、权限、偏移量、设备、文件inode、映射对象
第1行 [可读可执行] 是程序的文本段
第2行 [只读]………….不清楚
第3行 [可读可写] 是程序的数据段(包括初始化数据段和未初始化数据段)
第4行 ………………..堆空间
第16行 ………………栈空间
vdso 和 vsyscall ……………系统的快速调用
http://blog.csdn.net/maimang1001/article/details/17331731
用 ls -i 可查看源文件的inode,符合第5列;链接库在根分区下,我的是sda8,源程序在home下,我的是sda8,符合第4列。
3、程序的运行结果
从程序的入口地址main 0x400586 可以看出,maps文件的第一行确实是程序的代码段。
全局变量(初始化和未初始化)位于数据段,对应第3行
局部变量位于栈段
const 常量位于栈段
常字符串位于文本段
动态申请内存位于堆段
64位系统中应该有48根地址总线,低位:0~47位才是有效的可变地址,高位:48~63位全补0或全补1。一般高位全补0对应的地址空间是用户态,如上面的第1~18行。高位全补1对应的是内核态,如上面的第19行。这64位的地址空间并不能全部被使用(太多了),所以用户态和内核态之间会有未使用的空间(据说叫AMD64空洞)。
由上图的文件内容可以看出,从低地址到高地址内存布局依次是:
保留区 –> 文本段–>数据段—>堆—>共享库—>栈–>环境变量—>内核态
由pmap 命令和maps的显示可看出,栈和堆的中间部分是共享库,栈空间地址减小向下增长,堆空间地址增大向上增长。我觉得堆栈增长的时候,数据会先往紧挨的已分配内存anon中放,anon不够用时再继续增长,新分配内存。
dmesg 命令
显示系统启动时的信息