linux 内核内存布局(以64位系统为例)
64位系统理论可寻址范围位2的64次方,但实际一般用不到,故linux的可寻址范围位2的48次方;其中128T为内核空间,128T为用户空间。编译器把源程序编译为各个段(Segment),段是由一个或多个节(section)组成。linux下可执行文件是ELF格式,因此可以通过以下命令查看各个段或节的内存地址:
readelf -S memoryCkeck #查看可执行程序由多少个节以及每个节的地址
readelf -l memoryCheck #查看可执行程序头的地址以及大小,其中两个LOAD头是内存加载映射的重点
size memoryCheck # 查看程序BSS,Data,Text各个段的大小
cat /proc/${pid}/maps #同样可以查看程序的内存映射,其中没有名字的为匿名映射,一行为一个vm_area_struct
注:ELF格式的详细解析可以参考博文https://blog.csdn.net/sea_time/article/details/103190793
平常理解的程序加载情况如图1所示,但实际操作系统加载时,操作系统不关心加载的内容,反而更加关心加载内容的权限。所以编译器会把权限相同的节会尽可能放在一起,故实际加载时的格局如如2所示:
图1
图2
注:图中的.xxx即节的名称,查看ELF文件时可以看到;
实验验证内存映射情况
//memoryCheck.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
/*
查看进程中内存地址的分配
*/
int A; //全局变量未初始化
int B = 0; //全局初始化为0的变量
int C = 2; //全局初始化变量
static int D; //全局静态未初始化变量
static int E = 0; //全局静态初始化为0的变量
static int F = 4; //全局静态初始化变量
const int G = 5; //全局常亮
const char H = 6;
const char I = 7;
int main(void){
int a; //局部未初始化变量
int b = 0; //局部初始化为0的变量
int c = 2; //局部初始化变量
static int d; //局部静态未初始化变量
static int e = 0; //局部静态初始化为0的变量
static int f = 4; //局部静初始化变量
const int g = 5; //局部常量
char char1[] = "abcde"; //局部字符串数组,保存在栈上
char *cptr = "123456"; //p在栈上,123456在常量区
int *heap = malloc(sizeof(int)*4); //堆
printf("PID is:%d\n\n",getpid());
printf("int A A_addr = %p\n",&A);
printf("int B = 0 B_addr = %p\n",&B);
printf("int C = 2 C_addr = %p\n",&C);
printf("static int D D_addr = %p\n",&D);
printf("static int E = 0 E_addr = %p\n",&E);
printf("static int F = 4 F_addr = %p\n",&F);
printf("const int G = 4 G_addr = %p\n",&G);
printf("const char H = 6 H_addr = %p\n",&H);
printf("const char I = 7 I_addr = %p\n",&I);
printf("\n");
printf("int a a_addr = %p\n",&a);
printf("int b = 0 b_addr = %p\n",&b);
printf("int c = 2 c_addr = %p\n",&c);
printf("static int d d_addr = %p\n",&d);
printf("static int e = 0 e_addr = %p\n",&e);
printf("static int f = 4 f_addr = %p\n",&f);
printf("const int g = 5 g_addr = %p\n",&g);
printf("\n");
printf("char char1[] = 'abcde'\t\t\tchar1_addr = %p\n",char1);
printf("char char1[] = 'abcde'\t\t\t&char1_addr = %p\n",&char1);
printf("char *cptr = '1'\t\t\tcptr_addr = %p\n",&cptr);
printf("value of the cptr\t\t\tcptr_value = 0x%p\n",cptr);
printf("value of %p\t\t\tvalue_0x%p = %d\n",cptr,cptr,*cptr);
printf("int* heap = malloc(sizeof(int)*4)\theap__addr = %p\n",heap);
printf("int* heap = malloc(sizeof(int)*4)\t&heap__addr = %p\n",&heap);
pause();
/*程序结束运行后再回收堆内存,方便观察堆的地址*/
free(heap);
return 0;
}