用户区的虚拟地址包含三部分内容,分段+分页+分页偏移。
用户程序运行在内存中,内容会存储在不同内存区域,区域分为以下六种
- 代码区:存放函数体、代码的地方;
- 常量区:不可变的变量,如const定义的变量;
- 数据区:存放可执行文件中已初始化全局变量;存放静态变量和全局变量;
- BSS区:未初始化全局变量;
- 堆区:存放被动态分配的内存段;
- 栈区:存放临时创建的局部变量;
经测试,栈区的地址不一定比堆区地址大,比如在Ubuntu21.10(GCC 11.2)测试。
图:虚拟地址组成和分段组成
环境:gcc version 8.3.0 (Debian 8.3.0-6)?
目的:打印分段各个区域变量的内存地址。
#include <stdio.h>
#include <stdlib.h>
#define offset(a) (a & 0xFFF)
#define page(a) ((a >> 12) & 0x3FF)
#define segm(a) ((a>>22)&0x3FF)
#define PRINT(p) \
{ \
unsigned int t = (unsigned int)p; \
printf("ADDRESS:0x%08X,SEGMENT:0x%03X,PAGE:0x%03X,OFFSET:0x%03X NAME: %s\n",\
t, segm(t), page(t),offset(t),#p);\
}
int bss1[1024];
int data1[1] = {4545};
int bss2[1024];
int data2[1] = {998885};
const char* const1 = "const hello";
const char* const2 = "123";
void fun1(){}
void fun2(){}
int main()
{
int stack1[123] = {89748979};
int stack2[222]={0};
void * heap1 = malloc(1024);
void * heap2 = malloc(1024);
PRINT(stack1);
PRINT(stack2);
printf("-----------------------------------------------------------------------\n");
PRINT(heap1);
PRINT(heap2);
printf("-----------------------------------------------------------------------\n");
PRINT(bss1);
PRINT(bss2);
printf("-----------------------------------------------------------------------\n");
PRINT(data1);
PRINT(data2);
printf("-----------------------------------------------------------------------\n");
PRINT(const1);
PRINT(const2);
printf("-----------------------------------------------------------------------\n");
PRINT(fun1);
PRINT(fun2);
printf("-----------------------------------------------------------------------\n");
free(heap1);
free(heap2);
return 0;
}
打印出所有变量的地址,地址按照 “代码区->常量区->数据区->BSS区->堆区->栈区”方向增长。
ADDRESS:0x2E77DA40,SEGMENT:0x0B9,PAGE:0x37D,OFFSET:0xA40 NAME: stack1
ADDRESS:0x2E77D6C0,SEGMENT:0x0B9,PAGE:0x37D,OFFSET:0x6C0 NAME: stack2
-----------------------------------------------------------------------
ADDRESS:0x01C87260,SEGMENT:0x007,PAGE:0x087,OFFSET:0x260 NAME: heap1
ADDRESS:0x01C87670,SEGMENT:0x007,PAGE:0x087,OFFSET:0x670 NAME: heap2
-----------------------------------------------------------------------
ADDRESS:0x004040A0,SEGMENT:0x001,PAGE:0x004,OFFSET:0x0A0 NAME: bss1
ADDRESS:0x004050A0,SEGMENT:0x001,PAGE:0x005,OFFSET:0x0A0 NAME: bss2
-----------------------------------------------------------------------
ADDRESS:0x00404050,SEGMENT:0x001,PAGE:0x004,OFFSET:0x050 NAME: data1
ADDRESS:0x00404054,SEGMENT:0x001,PAGE:0x004,OFFSET:0x054 NAME: data2
-----------------------------------------------------------------------
ADDRESS:0x00402008,SEGMENT:0x001,PAGE:0x002,OFFSET:0x008 NAME: const1
ADDRESS:0x00402014,SEGMENT:0x001,PAGE:0x002,OFFSET:0x014 NAME: const2
-----------------------------------------------------------------------
ADDRESS:0x00401162,SEGMENT:0x001,PAGE:0x001,OFFSET:0x162 NAME: fun1
ADDRESS:0x00401169,SEGMENT:0x001,PAGE:0x001,OFFSET:0x169 NAME: fun2
-----------------------------------------------------------------------
参考:虚拟地址、线性地址、物理地址之间是如何转换的? - 知乎
|