『Linux从入门到精通』第 ⑯ 期 - 堆区、栈区真实存在吗?是操作系统在骗你罢了...

这篇具有很好参考价值的文章主要介绍了『Linux从入门到精通』第 ⑯ 期 - 堆区、栈区真实存在吗?是操作系统在骗你罢了...。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

『Linux从入门到精通』第 ⑯ 期 - 堆区、栈区真实存在吗?是操作系统在骗你罢了...,Linux从入门到精通,linux,运维,服务器

💐专栏导读

🌸作者简介:花想云 ,在读本科生一枚,C/C++领域新星创作者,新星计划导师,阿里云专家博主,CSDN内容合伙人…致力于 C/C++、Linux 学习。

🌸专栏简介:本文收录于 Linux从入门到精通,本专栏主要内容为本专栏主要内容为Linux的系统性学习,专为小白打造的文章专栏。

🌸相关专栏推荐:C语言初阶系列C语言进阶系列C++系列数据结构与算法

💐文章导读

本章我们将对程序地址空间进行讲解,理解虚拟地址的运作逻辑,认识虚拟地址与物理地址如何建立联系以及虚拟地址存在的意义~

『Linux从入门到精通』第 ⑯ 期 - 堆区、栈区真实存在吗?是操作系统在骗你罢了...,Linux从入门到精通,linux,运维,服务器

🐧引例

——为什么一个变量拥有两个不同的值?

之前在第一次认识fork时,我们就知道fork是用来创建子进程的。先来感受一段代码,我们定义一个全局变量g_val,在父进程与子进程中分别查看g_val的值以及它的地址。

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

int g_val = 100;

int main()
{
  pid_t id = fork();

  if(id < 0)
  {
    perror("fork");
    return 0;
  }
  else if(id == 0)
  {
    // 子进程
    while(1)
    {
       printf("我是子进程,pid=%d,ppid=%d,g_val=%d,&g_val=%d\n",getpid(),getppid(),g_val,&g_val);
       sleep(1);
    }
  }
  else 
  {
    // 父进程
    while(1)
    {
      printf("我是父进程,pid=%d,ppid=%d,g_val=%d,&g_val=%d\n",getpid(),getppid(),g_val,&g_val);
      sleep(1);
    }
  }

  return 0;
}

『Linux从入门到精通』第 ⑯ 期 - 堆区、栈区真实存在吗?是操作系统在骗你罢了...,Linux从入门到精通,linux,运维,服务器

如图所示,我们发现父子进程打印的值与地址完全相同。接着再来做测试,倘若在子进程中改变g_val的值,那么父进程中的g_val会不会一起跟着变化呢?

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

int g_val = 100;

int main()
{
  pid_t id = fork();

  if(id < 0)
  {
    perror("fork");
    return 0;
  }
  else if(id == 0)
  {
    // 子进程
    while(1)
    {
       printf("我是子进程,pid=%d,ppid=%d,g_val=%d,&g_val=%d\n",getpid(),getppid(),g_val,&g_val);
       sleep(1);
       g_val = 200;
    }
  }
  else 
  {
    // 父进程
    while(1)
    {
      printf("我是父进程,pid=%d,ppid=%d,g_val=%d,&g_val=%d\n",getpid(),getppid(),g_val,&g_val);
      sleep(1);
    }
  }
  return 0;
}

『Linux从入门到精通』第 ⑯ 期 - 堆区、栈区真实存在吗?是操作系统在骗你罢了...,Linux从入门到精通,linux,运维,服务器

结果如图所示,我们发现在子进程中修改变量的值,并不会影响父进程。我们知道进程具有独立性,父子进程之间互不影响好像也能说得过去,但是两个进程中g_val的地址都是相同的,一个地址怎么能存两个不同的值呢只能说明这两个地址其实并不是真正的物理地址。

对此我们可以得出结论:

  • 变量内容不一样,所以父子进程输出的变量绝对不是同一个变量;
  • 但它们地址值是一样的,说明该地址绝对不是物理地址!
  • 在Linux地址中,这种地址叫做 虚拟地址
  • 我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理。

接下来我们就来看看何为虚拟地址,它又为什么而存在。

🐦进程地址空间

或许我们之前都是这么看待内存的——
『Linux从入门到精通』第 ⑯ 期 - 堆区、栈区真实存在吗?是操作系统在骗你罢了...,Linux从入门到精通,linux,运维,服务器
如图所示,我们以前经常把内存区域作划分,并强调哪些变量应该存储在内存的哪些区域,其实这些区域的划分都是虚拟内存。

那么这些不同的区域(栈区、堆区、常量区等)又是如何进行划分的呢?

  • 进程地址空间本质上就是一个内核数据结构——struct mm_struct,由操作系统所管理。

我们知道内存的地址是连续的,也就意味着内存空间是一种线性结构。OS通过对内核数据结构的划分完成对内存区域的划分,例如:

struct mm_struct
{
	// 代码区
	long code_start; // [0,100]
	long code_end;
	// 初始化区域
	long init_start; // [200,500]
	long init_end;
	// 栈区
	long stack_start; //[x,x+n]
	long start_end;
	//...
}

🐦虚拟地址与物理内存的联系

虚拟地址是虚拟的、不存在的。可是我们所写的那些变量常量等等总得找个地方存起来吧。它们可都是实实在在的存储在物理内存上的。难道虚拟内存所有的内容都与物理内存一一对应吗?那当然是不可能的。

其实在虚拟内存与物理地址之间还存在这一个媒介——页表。它负责将虚拟地址与物理地址形成一种映射关系。

『Linux从入门到精通』第 ⑯ 期 - 堆区、栈区真实存在吗?是操作系统在骗你罢了...,Linux从入门到精通,linux,运维,服务器

🔔回答引例中的问题

有了上面的概念,我们现在可以清楚。子进程是被父进程创建出来的,所以子进程没有自己的代码和数据,只能继承自父进程。所以子进程与父进程中的g_val地址相同,现在我们知道这两个地址其实都是虚拟地址。

  • 父进程与子进程中的g_val会被页表映射到同一个物理地址。

有的小伙伴可能会疑问,不对呀,那这样刚才岂不是白讲了。这样的话我们改变子进程中的g_val,父进程不也会跟着变吗?

答案是,还差非常重要的一点没有提出来——写时拷贝

🔓写时拷贝

由于父进程和子进程的g_val一开始内容是完全相同的,没必要再新开一块空间存储一个重复的数据,避免浪费。所以,只要我们不修改g_val的值,两个g_val存在共用一块内存区域是合理的。

但是,一旦有一方需要修改g_val的值,为了避免一方修改会影响到另一方,此时OS会在内存中重新找到一块区域,将修改后的值放进去,并让页表将虚拟地址从旧的物理地址,映射到新的虚拟地址。这就叫做写时拷贝

『Linux从入门到精通』第 ⑯ 期 - 堆区、栈区真实存在吗?是操作系统在骗你罢了...,Linux从入门到精通,linux,运维,服务器

  • 写时拷贝概念图

『Linux从入门到精通』第 ⑯ 期 - 堆区、栈区真实存在吗?是操作系统在骗你罢了...,Linux从入门到精通,linux,运维,服务器

🐦虚拟地址存在的意义

举个例子,假设现在有100个班级,需要去一个水果商店免费水果。现在校长把任务交给了这100个班主任,让他们自行组织安排。班主任们并不需要考虑所有学生的人数或者男女比例或者强壮程度等等,只需要安排好自己班级的每个学生应该完成什么任务。这样,每个班主任各自拟好了一个任务安排表,并把它交给商店老板。商店老板会根据任务安排表,分配好每个班级应领取的水果数量、种类等等,到时候直接同时学生们过来取走即可。

在这个小例子中,学生就相当于代码和数据任务安排表相当于虚拟地址商店老板就相当于页表商店的水果相当于物理内存

假设不存在虚拟地址,也就是任务安排表不存在,商店老板就不能对着安排表做出规划,同学们与领取水果时,势必会杂乱无章甚至可能起了冲突。例如几个同学一起盯上了为数不多的榴莲…况且,同学们不知道商店中哪些水果可以拿。哪些已经被顾客预定了,不能拿…

对比这个例子,虚拟地址的作用有:

  • 防止地址随意访问,保护物理内存与其他进程(防止争抢水果)
  • 将进程管理与内存管理进行解耦合(班主任管学生,商店老板管水果)
  • 可以让进程以统一的视角看待自己的代码和数据(每个班主任只管自己的学生)
🔓malloc的本质

站在一个进程的角度

在很多时候,我们malloc申请了一块内存空间(现在我们知道这其实是虚拟内存),并没有立即对它进行使用,但是这块空间已经在某个变量的名下了,此时这块空间是不能再被其他变量所占用的,这是合理的。

站在OS的角度

那么当一个进程向OS申请内存时,请问OS是直接给它呢?还是在进程需要用的时候再给它呢?答案是在需要时再给它。

因为OS不允许有任何的浪费或者不高效的行为。假设进程一申请内存,OS就给它分配内存,但是进程又暂时不用。那么它自己不用也就算了,别的进程也用不了。一个进程这样做也就罢了,当大量的进程都这样做时就会造成严重的浪费与效率低下。

  • 其实,当我们的程序在编译完成后,就已经按照虚拟地址对代码和数据进行编制了

本章的内容就到这里了,如果觉得对你有帮助的话就支持一下博主吧~

『Linux从入门到精通』第 ⑯ 期 - 堆区、栈区真实存在吗?是操作系统在骗你罢了...,Linux从入门到精通,linux,运维,服务器
『Linux从入门到精通』第 ⑯ 期 - 堆区、栈区真实存在吗?是操作系统在骗你罢了...,Linux从入门到精通,linux,运维,服务器

点击下方个人名片,交流会更方便哦~
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓文章来源地址https://www.toymoban.com/news/detail-604238.html

到了这里,关于『Linux从入门到精通』第 ⑯ 期 - 堆区、栈区真实存在吗?是操作系统在骗你罢了...的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 【Linux从入门到精通】文件I/O操作(C语言vs系统调用)

    文章目录 一、C语言的文件IO相关函数操作 1、1 fopen与fclose 1、2 fwrite 1、3 fprintf与fscanf 1、4 fgets与fputs 二、系统调用相关接口 2、1 open与close 2、2 write和read 三、简易模拟实现cat指令 四、总结 🙋‍♂️ 作者:@Ggggggtm 🙋‍♂️ 👀 专栏:Linux从入门到精通  👀 💥 标题:文

    2024年02月13日
    浏览(49)
  • 『Linux从入门到精通』第 ⑫ 期 -深入了解冯诺依曼体系结构与操作系统(Operator System)

    🌸作者简介: 花想云 ,在读本科生一枚,C/C++领域新星创作者,新星计划导师,阿里云专家博主,CSDN内容合伙人…致力于 C/C++、Linux 学习。 🌸 专栏简介:本文收录于 Linux从入门到精通 ,本专栏主要内容为本专栏主要内容为Linux的系统性学习,专为小白打造的文章专栏。

    2023年04月23日
    浏览(83)
  • 『Linux从入门到精通』第 ⑫ 期 - 深入了解冯诺依曼体系结构与操作系统(Operator System)

    🌸作者简介: 花想云 ,在读本科生一枚,C/C++领域新星创作者,新星计划导师,阿里云专家博主,CSDN内容合伙人…致力于 C/C++、Linux 学习。 🌸 专栏简介:本文收录于 Linux从入门到精通 ,本专栏主要内容为本专栏主要内容为Linux的系统性学习,专为小白打造的文章专栏。

    2024年02月10日
    浏览(48)
  • 【Linux操作系统】探秘Linux奥秘:操作系统的入门与实战

    🌈个人主页: Sarapines Programmer 🔥 系列专栏: 《操作系统实验室》 🔖诗赋清音:柳垂轻絮拂人衣,心随风舞梦飞。 山川湖海皆可涉,勇者征途逐星辉。 目录 🪐1 初识Linux OS 🪐2 操作系统的入门与实战 🌍1. 实验目的 🌍2. 实验准备 🌍3. 实验内容 🌍4. 实验心得 📝总结

    2024年02月04日
    浏览(46)
  • Linux从入门到精通 --- 2.基本命令入门

    2.1 Linux的目录结构 Linux的目录结构是一个树型结构,没有盘符概念,只有一个根目录/,所有文件都在它下面 2.1.1 路径描述方式 Linux系统中,路径之间的层级关系用:/来表示 Linux系统中,路径之间的层级关系用:来表示 2.2 Linux命令入门 命令行:Linux终端,是一种命令提示符

    2024年04月10日
    浏览(52)
  • 【Linux从入门到放弃】Linux操作系统环境变量一文详解

    🧑‍💻作者: @情话0.0 📝专栏:《Linux从入门到放弃》 👦个人简介:一名双非编程菜鸟,在这里分享自己的编程学习笔记,欢迎大家的指正与点赞,谢谢!   提到环境变量,大家应该都不会陌生,不管学习Java还是Phython都需要去安装环境变量,尤其是我自己在做深度学习

    2024年02月05日
    浏览(50)
  • Linux从入门到精通 --- 1.初始Linux

    1.1 Linux的诞生 1991年由林纳斯 托瓦兹创立并发展至今称为服务器操作系统领域的核心系统。 1.2 Linux系统内核 Linux内核提供了系统的主要功能,甚至是开源免费的,任何人都可查看内核的源代码或是贡献源代码。 1.3 Linux系统发行版 内核无法被用户直接使用,需配合运用程序才

    2024年04月14日
    浏览(43)
  • Linux零基础快速入门到精通

    目录 一、操作系统概述   二、初始Linux Linux的诞生  ​编辑 Linux内核  Linux发行版   小结  三、虚拟机 认识虚拟机  虚拟化软件及安装 远程连接Linux系统  小结  扩展-虚拟机快照  四、Linux基础命令 查看命令帮助和手册(--help)  Linux的目录结构  Linux命令基础格式  ls命

    2024年02月13日
    浏览(50)
  • 操作系统的最强入门科普(Unix/Linux篇)

    大家好,我是小枣君。 今天这篇文章,我们来聊聊 操作系统( Operating System ) 。 说到操作系统,大家都不会陌生。我们天天都在接触操作系统——用台式机或笔记本电脑,使用的是windows和macOS系统;用手机、平板电脑,则是android(安卓)和iOS系统。 如果是从事信息通信行

    2024年02月04日
    浏览(55)
  • 【Linux从入门到精通】Linux常用基础指令(中)

        本篇文章接上篇文章(【Linux从入门到精通】Linux常用基础指令(上))进行详解。本章的指令相对较为重要,使用频率较高,难度相对上篇文章较难,也较为复杂。希望能对你的理解有所帮助。  文章目录 一、mv 指令 1、1 剪切和重命名普通文件  1、2 剪切和重命名目录

    2024年02月02日
    浏览(46)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包