验证进程地址空间的基本排布

111 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第27天,点击查看活动详情

【写在前面】

本文中会介绍很多结构化的知识,我们会通过很多例子里的角色和场景来对一个概念进行定位和阐述,让大家有一个宏观的认识,之后再循序渐进的填充细节,如果你一上来就玩页表怎么映射,那么你可能连页表存在的价值是什么都不知道,最后也只是竹篮打水。

一、回顾与纠正

C/C++ 内存布局这个概念比较重要,之前我们也涉及过 —— 我们在语言上定义的各种变量等在内存中的分布情况,如果没有听说过,那么你的 C/C++ 是不可能学好的。 在这里插入图片描述 上图表示的是内存吗 ❓

  其实我们曾经说过的 C/C++ 内存布局,严格来说是错误的,从今天开始我们应该称之为C/C++ 进程地址空间。为啥要故意说错呢,其实是因为方便理解,如果当时说 C/C++ 进程地址空间,那么不谈进程、地址、空间,就很容易误导大家。也就是说实际上要真正理解 C/C++ 的空间布局,光学习语言是远远不够的,还需要学习系统以及进程和内存之间的关系。

进程地址空间既然不是内存,那么栈、堆等这些空间的数据存储在哪 ???

  进程地址空间,会在进程的整个生命周期一直存在,直到进程退出,这也就解释了为什么全局变量具有全局属性。其实这些数据最后一定会存储于内存,只不过进程地址空间是需要经过某种转换才到物理内存的。

二、 验证进程地址空间的基本排布

#include<stdio.h>
#include<stdlib.h>
  
int g_unval;
int g_val = 100;
   
int main(int argc, char* argv[], char* env[])
{
	printf("code addr:         %p\n", main);
    const char* p = "hello bit!";
    printf("read only:         %p\n", p);
    static int a = 5;
    printf("static global val: %p\n", &a);
    printf("global val:        %p\n", &g_val);
    printf("global uninit val: %p\n", &g_unval);
    char* q1 = (char*)malloc(10);
    char* q2 = (char*)malloc(10);
    printf("heap addr:         %p\n", q1); 
    printf("heap addr:         %p\n", q2); 
    printf("p stack addr:      %p\n", &p);
    printf("q1 stack addr:     %p\n", &q1);
    printf("args addr:         %p\n", argv[0]);
    printf("args addr:         %p\n", argv[argc - 1]);
    printf("env addr:          %p\n", env[0]);
    
    return 0;                                                                                                                                                                           
}

💨结果:

在这里插入图片描述