• 程序地址空间--Linux


    01. 研究背景

    • ernel 2.6.32
    • 32位平台

    02. 程序地址空间分布

    1)空间布局图

    在我们学习c语言的时候我们都接触过下面这张空间布局图。
    
    • 1

    在这里插入图片描述

    可是我们对他并不理解!
    
    • 1

    2)上段代码,来感受一下。

    #include
    #include
    
    int a=1, b; 
    int main(int argc, char* argv[], char* env[])
    {
      printf("code addr:%p\n", main);
      
      printf("uninit global addr:%p\n", &b);
      printf("init global addr:%p\n", &a);
    
      char* heap_men = (char*)malloc(10);
      printf("heap addr:%p\n", heap_men);
      printf("stack addr:%p\n", &heap_men);
    
      int j=0;
      for( j=0; j<argc; ++j)
      {
        printf("argv[%d] addr:%p\n", j, argv[j]);
      }
    
      for(j=0; env[j]; ++j)
      {
        printf("env[%d] addr:%p\n", j, env[j]);
      }
    
      return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    有程序可知空间布局图,的确如此分布;共享区不好演示,此处就不演示了。
    在这里插入图片描述

    3)写时拷贝

    我们了解了以上内容。我们来看看下面一段代码。

    #include 
    #include 
    #include 
    int g_val = 0;
    int main()
    {
      pid_t id = fork();
      if(id < 0){
        perror("fork");
        return 0;
    
      }
      else if(id == 0){ //child,子进程肯定先跑完,也就是子进程先修改,完成之后,父进程再读取
      g_val=100;
      printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
      }else{ //parent
      sleep(3);
      printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
      }
      sleep(1);
      return 0;
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述
    通过上面我们明显发现输出结果

    • //与环境相关,观察现象即可

        child[15090]: 100 : 0x601058
        parent[15089]: 0 : 0x601058
      
      • 1
      • 2

    我们发现,父子进程,输出地址是一致的,但是变量内容不一样!能得出如下结论:

    1. 变量内容不一样,所以父子进程输出的变量绝对不是同一个变量
    2. 但地址值是一样的,说明,该地址绝对不是物理地址!
    3. 在Linux地址下,这种地址叫做 虚拟地址
    4. 我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理OS必须负责将 虚拟地址 转化成 物理地址。

    提醒:
    3. 写时拷贝我们这里粗讲,以后文章会详细介绍;现在我们只需了解就可以了。
    2. 写时拷贝:我们知道子进程是父进程的一份拷贝;由此父子进程的虚拟地址也是相同的。刚开始的时候两者虚拟地址映射同一块物理内存,当子进程的变量值发生改变的时候,就会立即开辟一块新的空间给子进程。此时两者虚拟地址虽然相同,但映射到实际的物理空间内存就不同了。

    03. 进程地址空间

    • 所以之前说‘程序的地址空间’是不准确的,准确的应该说成 进程地址空间 ,那该如何理解呢?看图!!!

    分页&虚拟地址空间

    在这里插入图片描述

    • 说明:
      • 上面的图就足矣说名问题,同一个变量,地址相同,其实是虚拟地址相同,内容不同其实是被映射到了不同的物理地址!

    扩展–只做本章了解。

    在这里插入图片描述
    提醒:

     函数调用实际是通过虚拟地址调用的。
     可执行程序其实编译的时候,内部已经有地址。
     地址空间不止是OS内部要遵守,其实编译器也要遵守。
     每一个函数和变量都有地址。
    
    • 1
    • 2
    • 3
    • 4

    我们使用Linux的时候,我们写的test.c文件会通过
    gcc -o mytest test.c这条指令生成mytest可执行文件.exe。
    通过编译器;
    此时mytest里面同样有内存,而且是虚拟地址;CPU调用的也是虚拟地址。CPU读到指令内部,使用的地址是虚拟地址,再通过映射找到物理地址。

    .
    .
    .
    感觉有所收获的话,友友们给小丁一个赞👍

  • 相关阅读:
    java Bigdecimal
    Ubuntu24.04基本配置
    QT信号与槽/窗口组件优化/使用QT制作QQ登录界面
    【Golang星辰图】Go语言之光照耀数据科学:揭开强大库的神秘面纱
    VS2017/2019均适用的opencv 快速通用免重复安装配置流程
    HSF概述
    Clickhouse 学习笔记(6)—— ClickHouse 分片集群
    Git远程分支操作
    [附源码]计算机毕业设计JAVA基于ssm的电子网上商城
    CTF入门学习笔记——Crypto密码(古典密码)
  • 原文地址:https://blog.csdn.net/Dingyuan0/article/details/126793783