Linux:进程地址空间

这篇具有很好参考价值的文章主要介绍了Linux:进程地址空间。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1.程序地址空间 

2.进程地址空间


1.程序地址空间 

我们在讲C/C++语言的时候,32位平台下,我们见过这样的空间布局图

Linux:进程地址空间,Linux,算法,linux,运维,地址空间

我们来验证一下这张图的正确性:

  int un_gval;
  int init_gval=100;
  
  int main(int argc, char* argv[],char* env[])
  {
      //代码区
      printf("code addr: %p\n",main);
      //字符常量区
      const char *str = "hello Linux";
      //*str = 'h';//不能修改因为字符常量区是被写入到代码区的,而代码区不能被修改
      printf("read only char addr: %p\n",str);
      //已初始化全局变量区
      printf("init global value addr: %p\n",&init_gval);
      
      //所谓的静态区就是已初始化全局变量区
      static int a ;   
      printf("stack addr: %p\n",&a);

      //已初始化全局变量区
      printf("uninit global value addr: %p\n",&un_gval);
      //堆区
      char *heap1 = (char*)malloc(100);
      char *heap2 = (char*)malloc(100);
      char *heap3 = (char*)malloc(100);
      char *heap4 = (char*)malloc(100);
      char *heap5 = (char*)malloc(100);            
      printf("heap1 addr: %p\n",heap1);//向地址增大方向增长    
      printf("heap2 addr: %p\n",heap2);    
      printf("heap3 addr: %p\n",heap3);    
      printf("heap4 addr: %p\n",heap4);    
      printf("heap5 addr: %p\n",heap5);                                                                                                                   
      //栈区                 
      printf("stack1 addr: %p\n",&heap1);    
      printf("stack2 addr: %p\n",&heap2);
      printf("stack3 addr: %p\n",&heap3);    
      printf("stack4 addr: %p\n",&heap4);   
      printf("stack5 addr: %p\n",&heap5);
  
      //命令行参数
      int i = 0;
      for(;argv[i];i++)
      {
          printf("argv[%d]:%p\n",i,argv[i]);
      }
      //环境变量
      for(i=0;env[i];i++)
      {
          printf("env[%d]:%p\n",i,env[i]);
      }
  
      return 0;
  }

运行结果:

Linux:进程地址空间,Linux,算法,linux,运维,地址空间

通过观察静态变量的位置,可以认为静态变量就是全局变量,只是静态变量只初始化一次,有作用域的限制。

这里栈区还有一个特点:我们平时定义结构体对象时,我们取地址都是返回整个结构体最低的地址,内部是使用低地址向高地址排列,使用的是起始地址加偏移量的访问方式,但是栈区整体还是先使用高地址在使用低地址。

那么这里就有一个问题了,这张图是真实的物理内存吗?

我们再来验证一下:

  #include <stdio.h>
  #include <stdlib.h>
  #include <unistd.h>

  int g_val = 100;
  
  int main()
  {
      pid_t id = fork();
      
      if(id==0)
      {
          //子进程
          int cnt = 5;
          while(1)
          {
              printf("child, pid:%d, ppid:%d, g_val:%d ,&g_val:%p\n",getpid(),getppid(),g_val,&g_val);
              sleep(1);
              if(cnt == 0)
              {
                  g_val = 200;
                  printf("child change g_val: 100->200\n");
              }
              cnt--;
          }
      }
      else 
      {
          //父进程
          while(1)
          {
              printf("father, pid:%d, ppid:%d, g_val:%d ,&g_val:%p\n",getpid(),getppid(),g_val,&g_val);                                                   
              sleep(1);
          }
  
      }
  
      return 0;
  }

运行上面代码的结果:

Linux:进程地址空间,Linux,算法,linux,运维,地址空间

什么意思呢?就是我们定义了一个全局变量 g_val,然后我们通过 fork() 创建了一个子进程,让子进程修改了全局变量。我们之前文章中提到过,因为进程之间要保证数据的独立性,父进程的数据子进程也要有一份,而Linux采用写时拷贝,所以在子进程没有修改全局变量值时,父进程和子进程的全局变量地址相同可以理解。但是子进程对全局变量做修改后,写时拷贝应该重新申请一块空间来存放修改后的值,但是根据运行结果我们发现地址还是相同的,子进程全局变量的地址并没有改变,同一个地址竟然读出不同的值?所以我们可以大胆推测我们看到的地址并不是真正的物理地址。

得出结论:

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

2.进程地址空间

2.1 操作系统如和将虚拟地址转换为物理地址

所以之前说“程序的地址空间”是不准确的,准确的应该说成 进程地址空间 ,那该如何理解呢?

每一个进程运行之后,都会有一个进程地址空间的存在!都要在系统层面都要有自己的页表映射结构
Linux:进程地址空间,Linux,算法,linux,运维,地址空间

在C/C++中,变量在编译形成可执行程序后,就没有变量名的概念了,都是地址。

2. 什么是地址空间?什么是区域划分?

地址空间也要被OS管理起来!!每一个进程都要有地址空间,系统中,一定要对地址空间做管理。如何管理地址空间呢? 也是通过之前文章提过的先描述,在组织。所以地址空间最终一定是内核的数据结构对象,就是一个内核结构体。

在这个结构体中,分别有每个空间如栈区,堆区的开始和结束位置。

在Linux中,这个进程/虚拟地址空间的东西,叫做:struct mm_struct

struct mm_struct
{
    long code start;
    long code_end;
    long data_start;
    long data_end;
    long heap_start;
    long heap_end; 
    long stack_start;
    long stack_end;
    // ...
}

进程PCB Linux 中的struct task_struct 中也是有指针指向mm_struct的。 

3.为什么要有地址空间

  1. 让进程以统一的视角看待内存,所以任意一个进程,可以通过地址空间+页表可以将乱序的内存数据,变成有序,分门别类的规划好,使得无序边有序
  2. 存在虚拟地址空间,因为页表中有访问权限字段,可以有效的进行进程访问内存的安全检查,比如我们无法修改字符常量的内容,是因为页表访问权限是只读。
  3. 将进程管理和内存管理进行解耦
  4. 通过页表让进程映射到不同的物理内存处,从而实现进程的独立性!所以每一个进程都认为自己可以使用4GB的空间,但是真实的物理空间只有4GB,一个进程并不知道其他进程的存在。
  5. CPU中也有一个CR3寄存器来保存页表的地址,这个地址是真实的物理地址。

扩展问题

我们如果在玩一些大型游戏时,游戏所需要的内存非常大,我们之前学习过 进程 = 内核数据结构体PCB+程序的代码和数据,我们把游戏加载到内存中时,是把所有的代码和数据都拷贝过来吗?根据我们呢的常识,显然不是这样的,因为我们得内存很小,为什么游戏还是可以运行的呢?因为页表中还有是否分配空间和是否有内容的字段,00,表示既没有分配空间也没有内容,我们游戏一次只加载一部分代码和数据,当CPU执行完这段代码时,要执行下面代码,操作系统就会将上面字段改为00,出现缺页中断,然后再去磁盘中拷贝接下来的代码和数据,释放执行完的代码和数据,这样就可以使得我们得游戏可以正常运行。

本篇结束!文章来源地址https://www.toymoban.com/news/detail-763502.html

到了这里,关于Linux:进程地址空间的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Linux】—— 进程地址空间

    序言: 在上篇中,我们讲解了关于进程优先级的概念。本期,我将给大家介绍的是关于进程地址空间的话题。 目录 (一)程序地址空间回顾 (二)代码演示 (三)进程地址空间的引入 总结 我们在学习C/C++语言的时候,大家可能都见过这样的空间布局图: 一个程序有哪些

    2024年02月15日
    浏览(30)
  • Linux--进程地址空间

    1.线程地址空间 所谓进程地址空间(process address space),就是从进程的视角看到的地址空间,是进程运行时所用到的虚拟地址的集合。 简单地说,进程就是内核数据结构和代码和本身的代码和数据,进程本身不能访问物理地址,之时候就需要有一个中间媒介,就是地址空间,

    2024年02月11日
    浏览(29)
  • 【Linux】深挖进程地址空间

    作者简介:დ旧言~,目前大二,现在学习Java,c,c++,Python等 座右铭:松树千年终是朽,槿花一日自为荣。 目标:熟悉【Linux】进程地址空间 毒鸡汤:也许有一天,你发觉日子特别的艰难,那可能是这次的收获特别的巨大。 望小伙伴们点赞👍收藏✨加关注哟💕💕      

    2024年02月03日
    浏览(26)
  • 『 Linux 』进程地址空间概念

    在c/C++中存在一种 内存 的概念; 一般来说一个内存的空间分布包括 栈区 , 堆区 , 代码段 等等; 且内存是 自底向上 (由 0x00000000 至 0xFFFFFFFF ); 以该图为例: 该图即为常见的内存分布图; 正文代码段 正文代码段所存放的数据 一般为函数体的二进制代码 ; 已初始化数据区 已初始化

    2024年02月03日
    浏览(29)
  • 浅析Linux进程地址空间

    现代处理器基本都支持虚拟内存管理,在开启虚存管理时,程序只能访问到虚拟地址,处理器的内存管理单元(MMU)会自动完成虚拟地址到物理地址的转换。基于虚拟内存机制,操作系统可以为每个运行中的进程创建独享的虚拟地址空间,在这个空间中执行的程序,无法感知

    2024年01月20日
    浏览(29)
  • 【Linux】理解进程地址空间

    🍎 作者: 阿润菜菜 📖 专栏: Linux系统编程 ​我们在学习C语言的时候,都学过内存区域的划分如栈、堆、代码区、数据区这些。但我们其实并不真正理解内存 — 我们之前一直说的内存是物理上的内存吗? 我们先看一段测试代码: 运行结果: 我们可以注意到子进程的变量

    2024年02月02日
    浏览(39)
  • Linux:进程地址空间

    目录 1.程序地址空间  2.进程地址空间 我们在讲C/C++语言的时候,32位平台下,我们见过这样的空间布局图 我们来验证一下这张图的正确性: 运行结果: 通过观察静态变量的位置,可以认为静态变量就是全局变量,只是静态变量只初始化一次,有作用域的限制。 这里栈区还

    2024年02月04日
    浏览(29)
  • 【Linux】进程理解与学习Ⅳ-进程地址空间

    环境:centos7.6,腾讯云服务器 Linux文章都放在了专栏:【 Linux 】欢迎支持订阅 🌹 相关文章推荐: 【Linux】冯.诺依曼体系结构与操作系统 【Linux】进程理解与学习Ⅰ-进程概念 浅谈Linux下的shell--BASH 【Linux】进程理解与学习Ⅱ-进程状态 【Linux】进程理解与学习Ⅲ-环境变量 在

    2023年04月14日
    浏览(62)
  • 【Linux取经路】初探进程地址空间

    之前在介绍 fork 函数的时候说过该函数返回了两次,至于为什么会返回两次,以及 fork 函数是如何做到返回两次的,在【Linux取经路】揭秘进程的父与子一文中已经做了详细的解释,忘了小伙伴可以点回去看看。在解释一个变量怎么会有两个不同值的时候,当时的说法是由于

    2024年01月21日
    浏览(33)
  • 【Linux进行时】进程地址空间

    我们在讲C语言的时候,老师给大家画过这样的空间布局图,但是我们对它不了解 我们写一个代码来验证Linux进程地址空间 这里没什么问题,就是他们的g_valule 和其地址都是一样的, 我们将代码调整一下,让子进程的g_value++ 我们可以发现子进程的g_value变了,但是父进程没有

    2024年02月08日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包