虚幻or现实?堆区、栈区真实存在吗?是操作系统在骗你罢了...

在这里插入图片描述

文章目录

  • 💐专栏导读
  • 💐文章导读
    • 🐧引例
  • 🐦进程地址空间
  • 🐦虚拟地址与物理内存的联系
      • 🔔回答引例中的问题
        • 🔓写时拷贝
  • 🐦虚拟地址存在的意义
        • 🔓malloc的本质

💐专栏导读

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

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

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

💐文章导读

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

在这里插入图片描述

🐧引例

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

之前在第一次认识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;
}

在这里插入图片描述

如图所示,我们发现父子进程打印的值与地址完全相同。接着再来做测试,倘若在子进程中改变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;
}

在这里插入图片描述

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

对此我们可以得出结论:

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

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

🐦进程地址空间

或许我们之前都是这么看待内存的——
在这里插入图片描述
如图所示,我们以前经常把内存区域作划分,并强调哪些变量应该存储在内存的哪些区域,其实这些区域的划分都是虚拟内存。

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

  • 进程地址空间本质上就是一个内核数据结构——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;
	//...
}

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

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

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

在这里插入图片描述

🔔回答引例中的问题

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

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

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

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

🔓写时拷贝

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

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

在这里插入图片描述

  • 写时拷贝概念图

在这里插入图片描述

🐦虚拟地址存在的意义

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

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

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

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

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

🔓malloc的本质

站在一个进程的角度

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

站在OS的角度

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

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

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

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

在这里插入图片描述
在这里插入图片描述

点击下方个人名片,交流会更方便哦~
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/21758.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

装饰者设计模式解读

问题引进 星巴克咖啡订单项目&#xff08;咖啡馆&#xff09;&#xff1a; 1) 咖啡种类/单品咖啡&#xff1a;Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡) 2) 调料&#xff1a;Milk、Soy(豆浆)、Chocolate 3) 要求在扩展新的咖啡种类时&#x…

接口测试全流程扫盲,让我看看有哪些漏网之鱼

目录 扫盲内容&#xff1a; 1.什么是接口&#xff1f; 2.接口都有哪些类型&#xff1f; 3.接口的本质及其工作原理是什么&#xff1f; 4.什么是接口测试&#xff1f; 5.问什么要做接口测试&#xff1f; 6.怎样做接口测试&#xff1f; 7.接口测测试点是什么&#xff1f;…

一些云原生开源安全工具介绍

本博客地址&#xff1a;https://security.blog.csdn.net/article/details/130789465 一、Kubernetes安全监测工具kube-bench kube-bench是一个用Golang开发的、由Aqua Security发布的自动化Kubernetes基准测试工具&#xff0c;它运行CIS Kubernetes基准中的测试项目。这些测试…

MySQL高级篇第一天

目录 一、索引 二、索引结构 三、索引分类 四、索引语法 五、索引设计原则 六、视图 七、存储过程与概述 八、触发器 九、总结 一、索引 &#xff08;一&#xff09;索引概述 索引是一种能够帮组Mysql高效的从磁盘上查询数据的一种数据结构&#xff0c;这些数据结构以某…

用WaveNet预测(Adapted Google WaveNet-Time Series Forecasting)

目录 剧情简介: 数据来源 加载数据 分割数据和可视化 时间序列的多元波网模型:实现(多步预测) 创建模型 创建数据集 数据准备 1- Training dataset preparation 2- Validation dataset preparation Train the Model with TPU: 使用经过训练的适应Google WaveNet预测…

YOLO NAS note 1

Git Hub: https://github.com/Deci-AI/super-gradients Yolo-Nas 的代码比YOLO v8 还恐怖。之前的YOLO数据可以通过&#xff1a; coco_detection_yolo_format_train&#xff0c; 和 coco_detection_yolo_format_val 自动转。 这里写目录标题 Train数据获取数据增强训练criteri…

ChatGPT 提问,软件杂项部分

堆内存与栈内存一般分别 有多少 ChatGPT 堆内存和栈内存的大小取决于操作系统和编译器的限制以及程序的运行环境。以下是一些常见的默认大小范围&#xff0c;但请注意这些值可以因环境而异&#xff1a; 栈内存大小&#xff1a; Windows平台&#xff1a;默认情况下&#xff…

CNN实现手写数字识别(Pytorch)

CNN结构 CNN&#xff08;卷积神经网络&#xff09;主要包括卷积层、池化层和全连接层。输入数据经过多个卷积层和池化层提取图片信息后&#xff0c;最后经过若干个全连接层获得最终的输出。 CNN的实现主要包括以下步骤&#xff1a; 数据加载与预处理模型搭建定义损失函数、优…

应用现代化中的弹性伸缩

作者&#xff1a;马伟&#xff0c;青云科技容器顾问&#xff0c;云原生爱好者&#xff0c;目前专注于云原生技术&#xff0c;云原生领域技术栈涉及 Kubernetes、KubeSphere、KubeKey 等。 2019 年&#xff0c;我在给很多企业部署虚拟化&#xff0c;介绍虚拟网络和虚拟存储。 2…

微服务架构初探

大家好&#xff0c;我是易安&#xff01;我们今天来谈一谈微服务架构的前世今生。 我们先来看看维基百科是如何定义微服务的。微服务的概念最早是在2014年由Martin Fowler和James Lewis共同提出&#xff0c;他们定义了微服务是由单一应用程序构成的小服务&#xff0c;拥有自己的…

建立在Safe生态的—GameFi SocialFi双赛道项目No.1头号玩家

最近大家关注的重点在BRC-20和MEME项目&#xff0c;人们似乎更在意短期的投机回报。而在这之外&#xff0c;一个web3的游戏——No.1头号玩家却得到了大量的玩家支持。 据了解&#xff0c;No.1是一个GameFi & SocialFi的双赛道web3游戏&#xff0c;中文名称为头号玩家。它是…

光纤衰减器作用及使用说明

在光纤通信中&#xff0c;光信号的强度过大或过小都会对信号的传输和接收产生不良的影响&#xff0c;因此光衰减器在光通信系统中起到了重要的作用。那什么是光衰减器呢&#xff1f;它又有什么作用呢&#xff1f;下面跟着小易一起来了解一下吧&#xff01; 一、什么是光纤衰减…

HUSTOJ中添加初赛练习系统

文章目录 0. 基于hustoj二开的初赛练习系统&#xff0c;QQ4705852261. 主界面2. 练习界面3. 模拟考试界面4. 查看试卷回放5. 后台操作界面6. 后台试题分类-列表7.后台试题-列表8. 后台试题-添加9. 后台试卷结构-设置 0. 基于hustoj二开的初赛练习系统&#xff0c;QQ470585226 …

[笔记]渗透测试工具Burpsuit《一》Burpsuit介绍

文章目录 前言一、安装配置1.1 环境1.2 安装过程1.3 科技过程 二、常用功能2.1 Manual penetration testing features2.2 Advanced/custom automated attacks2.3 Automated scanning for vulnerabilities2.4 Productivity tools2.5 Extensions 三、拓展功能 前言 Burp Suite(b…

一、预约挂号微服务模块搭建

文章目录 一、预约挂号微服务模块搭建1、项目模块构建2、sql资源3、构建父工程&#xff08;yygh-parent&#xff09;3.1、添加配置pom.xml 4、搭建common父模块4.1、搭建common4.2、修改配置pom.xml 5、搭建common-util模块5.1、搭建common-util5.2、修改配置pom.xml5.3、添加公…

ELK的安装部署与使用

ELK的安装与使用 安装部署 部署环境&#xff1a;Elasticsearch-7.17.3 Logstash-7.17.3 Kibana-7.17.3 一、安装部署Elasticsearch 解压目录&#xff0c;进入conf目录下编辑elasticsearch.yml文件&#xff0c;输入以下内容并保存 network.host: 127.0.0.1 http.port: 9200…

计算机网络实验(ensp)-实验10:三层交换机实现VLAN间路由

目录 实验报告&#xff1a; 实验操作 1.建立网络拓扑图并开启设备 2.配置主机 1.打开PC机 配置IP地址和子网掩码 2.配置完成后点击“应用”退出 3.重复步骤1和2配置每台PC 3.配置交换机VLAN 1.点开交换机 2.输入命名&#xff1a;sys 从用户视图切换到系统视图…

Jenkins版本升级

Jenkins版本过低的时候&#xff0c;一些插件无法升级&#xff0c;会引发一系列错误&#xff0c;这个时候我们就要升级版本了 一、下载更新包 第一种方式&#xff1a;Jenkins页面下载最新包 第二种官网上下载war包(Jenkins官网) 二、打开服务器搜索jenkins.war路径 1、如果Jenk…

SQL Backup Master 6.3.6 Crack

SQL Backup Master 能够为用户将 SQL Server 数据库备份到一些简单的云存储服务中&#xff0c;例如 Dropbox、OneDrive、Amazon S3、Microsoft Azure、box&#xff0c;最后是 Google Drive。它能够将数据库备份到用户和开发者的FTP服务器上&#xff0c;甚至本地机器甚至网络服务…

这几款实用的电脑软件推荐给你

软件一&#xff1a;TeamViewer TeamViewer是一款跨平台的远程控制软件&#xff0c;它可以帮助用户远程访问和控制其他计算机、服务器、移动设备等&#xff0c;并且支持文件传输、会议功能等。 TeamViewer的主要功能包括&#xff1a; 远程控制&#xff1a;支持远程访问和控制…