【Linux】进程地址空间(带你认清内存的本质)

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

       🔥🔥 欢迎来到小林的博客!!
      🛰️博客主页:✈️小林爱敲代码
      🛰️博客专栏:✈️Linux之路
      🛰️社区 :✈️ 进步学堂
      🛰️欢迎关注:👍点赞🙌收藏✍️留言

💖进程地址空间

我们在学习C语言的时候,应该都知道这个内存空间图。

【Linux】进程地址空间(带你认清内存的本质)

但其实我们对它并不了解,为什么呢?我们用一段代码来感受一下!

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

int g_val = 100;

int main()
{
  int pid = fork(); //创建子进程
  if(pid == 0)
  {
    //child
    int count = 5;
    while(count)
    {
      printf("i am child , g_val = %d, &g_val = %p\n",g_val,&g_val);

      if(count == 3)
      {
        //修改数据
        printf("******开始修改数据*******\n");
        printf("i am child , g_val = %d, &g_val = %p\n",g_val,&g_val);
        g_val = 200;
        printf("******修改数据done*******\n");
      }
      count--;
      sleep(1);
    } 
    
  }
  else if(pid > 0)
  {
    //parent
    while(1)
    {
      printf("i am father , g_val = %d, &g_val = %p\n",g_val,&g_val);
      sleep(1);
    }
  }
  else 
  {
    //erro
    perror("fork:");
  }
  return 0;
}

我们这个代码的主体逻辑是,创建一个全局变量。然后再创建一个子进程,随后打印全局变量的值和地址,在子进程特定的时候修改这个全局变量。

那么我们来看看运行结果吧!

【Linux】进程地址空间(带你认清内存的本质)

我们可以发现,g_val的值被修改了! 但是它们的地址还是一样的。这是怎么回事!???

我们都知道,父进程和子进程如果没有发生数据修改,那么会共用同一份数据。如果有一方的数据发生了修改,那么就会写实拷贝一份。所以此时的父进程和子进程各有一份属于自己的数据,既然有2份数据那么就说明有2个g_val。2个独立进程的g_val变量用了同一块内存空间,这合理吗?完全不合理!!!2个进程用同一块空间,这不就起冲突了。这是为什么呢??

再探索这个问题之前,先给大家讲个小故事,方便大家理解。

在美国有一个大富翁,他有三个私生子,这三个私生子互相不认识。而这个大富翁有100亿美金。

【Linux】进程地址空间(带你认清内存的本质)

然后这个时候,大富翁对私生子小A说:等我老了,就由你来继承我100亿财产吧。这时小A就以为这100亿是他的了。然后大富翁又对私生子小B说:等我老了,你来继承我的100亿吧。 小B听了高兴坏了,也以为这100亿是他的了。然后大富翁又对私生子小C说了同样的话。 所以,大富翁给他的所有私生子都花了一张大饼。告诉他们,他们未来都会继承这100亿。所以大富翁的私生子,都认为自己有100亿可以花,就可以按照这100亿来为自己分配生活。

【Linux】进程地址空间(带你认清内存的本质)

而这里面的大富翁,就是操作系统,私生子就是进程,大饼就是进程地址空间,那么这100亿美金,就是我们的物理内存。而我在之前的篇章里说过,进程的本质其实就是 描述进程的结构体(PCB)+代码数据。那么进程地址空间是否在PCB里呢?答案当然是的。也就是说每个进程都会有一个进程地址空间,每个私生子都认为自己独占了大富翁的100亿美金,所以每个进程都认为自己独占了物理内存。 所以,我们的**进程地址空间,也被我们称之为虚拟内存。**那么为什么会打印相同地址?我们先了解一些东西,再最侯为大家总结结论。

💖进程地址空间是什么?

那么进程地址空间是什么呢?地址空间本质是内核中的一种数据类型,在Linux内核中,它是一个struct mm_struct的结构体。

【Linux】进程地址空间(带你认清内存的本质)

也就说,我们程序的内存划分,本质上是一个区域!!

【Linux】进程地址空间(带你认清内存的本质)

进程地址空间的划分

那么进程地址空间是怎么划分的?

打个比方:

假如你现在是一名小学生,你的同桌是一名爱干净的小女孩。而你一名爱流鼻涕不讲卫生的小男孩,这时侯,你的同桌嫌弃你。假设你俩的桌子长100cm,此时你的同桌在50cm的地方画了一根三八线,跟你划清界限。那么此时你还能不能把东西放到你同桌所在的区域?当时是不能了!假设你的区域是 0 - 50cm的地方,那么你的东西只能放在0-50区间。假设这时候有一把尺子,你想把你的橡皮擦放在第38cm的地方,于是你就拿尺子量出了38cm,把橡皮放在这个位置上。这里面呢,你和你同桌,充当的是一块区域,而这把尺子,是进程地址空间,橡皮擦,则是你的数据。你要把数据放在指定的地方,那么就需要进程地址空间充当尺子。为什么你知道桌子是100cm?因为有尺子,所以你才知道桌子是100cm。所以你要划分区域,也需要进程地址空间充当尺子来划分区域。

struct mm_struct
{
	unsigned int code_start; //代码段起始地址
	unsigned int code_end;  //代码段结束地址
	
	unsigned int init_data_start;//初始化变量区起始地址.
	unsigned int init_data_end;//初始化变量区结束地址
	
	unsigned int uninit_data_start;//未初始化变量区起始地址.
	unsigned int uninit_data_end;//未初始化变量区结束地址
	
	unsigned int heap_start;//堆区起始地址.
	unsigned int heap_end;//堆区结束地址
	
	.....
	unsigned int stack_start;//堆区起始地址.
	unsigned int stack_end;//堆区结束地址
}

每个进程都认为地址空间的划分是按照4GB的空间划分的,而地址空间上进行区域划分的位置,是虚拟地址!虽然这里只要start和end,但是每个进程都可以认为mm_struct 代表整个内存,且所有的地址为0x00000000 -> 0xFFFFFFFF。

虚拟内存转换成物理内存

既然每个进程都有一块地址空间,而程序里面的数据和代码都是根据进程地址空间存放。那么我们的系统调用它时是如何为它分配地址的呢?如何把对应的数据放到物理内存的呢?

那是因为物理内存和虚拟内存之间,有一张页表。

【Linux】进程地址空间(带你认清内存的本质)

而页表的本质就是哈希表。通过虚拟内存来映射物理内存。也就说,每一个进程地址空间,都会有一张对应的页表。意思就是每一个进程都会有一张页表,通过页表的虚拟地址,就可以找到对应的物理内存。从而操作系统对物理内存进行操作。

💖为什么要有进程地址空间?

1. 通过添加一层软件层,完成有效的对进程操作内存进行风险管理(权限管理),本质的目的是为了,保护物理内存以及各个进程的数据安全。

比如:

先给大家放一段代码。

int main()
{
	const char* str = "hello world";
    str = "HW";
	return 0;
}

这段代码会报错,为什么呢?因为 str它所处的内存空间是常量区。通过虚拟内存映射到真实的物理地址之后,它的权限是只读权限。当你修改它时,因为你不具备写权限,所以操作系统会直接把你干掉。这也是为什么要有进程地址空间的原因。如果没有进程地址空间,那么就无法进行权限管理,那么即使是常量也可以被修改!这是非常严重的!而有了进程地址空间之后,你能不能修改,全部取决于操作系统让不让你修改!

2. 将内存申请和内存使用的概念在时间上划分清除,通过虚拟地址空间,来屏蔽底层申请的过程,达到进程读写内存和OS进行内存管理操作,进行软件上面的分离!

先抛出一个问题:假如我们申请5000个字节,我们立马能使用这5000字节吗??

答案是:不一定,可能会存在暂时不会全部使用,甚至暂时不使用的情况。

因为,在OS(操作系统)的角度上,如果空间立马就给你的话,是不是就意味着,整个系统会有一部分空间,本来可以先给其他进程立马使用,现在却被你闲置着?说简单点就是这个进程现在正茅坑,但丝毫没有要拉屎的意思。这种做法是人人恨之的,所以操作系统不一定会立马给你使用。

打个比方:比如你要开学了,你和你老爹要8000块钱的学费。但是你还有一星期才开学呢,于是你老爹说:好,我知道了,开学前一天给你。 你像你老爸要了8000块钱,你老爸对应给你了。这就就相当于你申请了8000字节的空间。但是你还有一星期才开学,也就是你这8000块钱暂时用不上,你这8000字节也暂时用不上。所以你爸说等你开学前一天的时候给你,而操作系统也在进程要使用的时候,给进程真实的物理内存。

【Linux】进程地址空间(带你认清内存的本质)

而这和我们的写实拷贝非常的像,数据不改变就共用同一份数据,改变就拷贝一份。

3.站在CPU和应用层的角度,进程统一可以看做统一使用4GB空间,而且每个空间区域的相对位置,是比较确定的!OS最终这样设计的目的,达到了一个目标:每个进程都认为自己是独占系统资源的!进程具有独立性的!

这种情况也就是我们开头演示的那样,为什么2个进程的g_val的地址是相同,而值是不同的。这是因为**子进程创建是以父进程为模板创建的,所以子进程也会继承父进程的页表。**在子进程没有对g_val的值进行修改时,父子进程共享一份数据。而一旦子进程对g_val的值进行修改,那么在OS会对g_val的数据进行一份拷贝(写实拷贝)。且让子进程页表映射到g_val的值映射至新拷贝后的物理地址。这样子,即使它们的g_val的地址是相同的,但是在它们在页表 g_val的数据 是映射到不同的物理地址。

【Linux】进程地址空间(带你认清内存的本质)

最后,为什么下面str1和str2的地址是相等的?

#include<stdio.h>
int main()
{
	const char* str1 = "hello world";
	const char* str2 = "hello world";
	printf("str1 的地址是: %p",str1);
	printf("str2 的地址是: %p",str2);
}

如上代码,我们会发现str1和str2的地址是一样的,可是它们不是同一个变量啊,为什么?因为str1和str2都在常量区。也就是说操作系统只给了这个区域可读权限,所以操作系统认为,对于只有可读的数据,操作系统只需要维护一份即可。文章来源地址https://www.toymoban.com/news/detail-464658.html

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

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

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

相关文章

  • Linux之进程(四)(进程地址空间)

    目录 一、程序地址空间 二、进程地址空间 1、概念 2、写时拷贝 3、为什么要有进程地址空间 四、总结 我们先来看看下面这张图。这张图是我们在学习语言时就见到过的内存区域划分图。  下面我们在Linux下看一看内存区域是不是也是这么划分的。 可见在Linux下也是符合上面

    2024年02月04日
    浏览(29)
  • 【Linux】进程周边006之进程地址空间

      👀 樊梓慕: 个人主页  🎥 个人专栏: 《C语言》 《数据结构》 《蓝桥杯试题》 《LeetCode刷题笔记》 《实训项目》 《C++》 《Linux》 🌝 每一个不曾起舞的日子,都是对生命的辜负 目录 前言 1.程序地址空间 1.1验证地址空间的排布  1.2利用fork函数观察当子进程修改某个共

    2024年02月04日
    浏览(28)
  • 【Linux】进程>环境变量&&地址空间&&进程调度

    主页: 醋溜马桶圈-CSDN博客 专栏: Linux_醋溜马桶圈的博客-CSDN博客 gitee :mnxcc (mnxcc) - Gitee.com 目录 1.环境变量 1.1 基本概念 1.2 常见环境变量  1.3 查看环境变量方法  1.4 和环境变量相关的命令 1.5 环境变量的组织方式 1.6 通过代码如何获取环境变量 1.6.1 命令行第三个参数 1

    2024年04月15日
    浏览(41)
  • 浅析Linux进程地址空间

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

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

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

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

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

    2024年02月04日
    浏览(30)
  • 【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日
    浏览(30)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包