【进程地址空间】地址空间理解存在原因 | 深入理解页表写时拷贝虚拟地址

news2024/9/28 5:30:35

目录

地址空间深入理解

划分区域

理解地址空间

地址空间存在的意义

意义1

意义2

意义3

理解页表和写时拷贝

页表

写时拷贝

OS识别错误 

理解虚拟地址

fork解释


上篇我们简单的学习了进程地址空间/页表/物理地址/虚拟地址/写时拷贝等概念。本篇深入理解下。

地址空间深入理解

划分区域

在我们初高中的时候,我们喜欢和我们同桌划分区域,是基于桌子进行区域的划分, 也可做出区域的调整。计算机语言是怎样表达的呢❓回答:结构体。

  • 地址空间本质是内核的一个struct结构体,内部很多的属性都是表示start,end的范围。
  • 地址空间浅显的理解:struct mm_struct (里面很多属性/字段/start&end)
  • 区域当中很多小的区域,每个区域都有自己的区域位置和范围,每个位置都可以被我们直接使用。不能简单的理解为限定了一段的区域范围。

 

 

理解地址空间

小故事tips:

  • 大富翁有很多个私生子(大富翁有10个亿)
  • 大富翁的私生子彼此不知道对方的存在,认为自己是独子(都认为自己可以继承这10个亿)
  • 每个私生子无论学习和工作都会向大富翁要小钱(每个人都认为相较于10个亿,自己要的钱是毛毛雨)
  • 所以,大富翁给每一个私生子画了一个大饼,承诺10个亿的遗产给他。
  • 大饼有10个亿,10个给每个私生子都是随便用。
  • 10个亿中的任何一块钱都可用。

  • 私生子:进程
  • 大富翁:操作系统OS
  • 物理内存:10个亿
  • 进程地址空间:大饼10个亿
  • 若私生子很多,大饼很多。都需要被管理起来。
  • 先描述再组织。
  • 同上,内存中任何一块空间都可以被OS使用(进程地址空间),取空间范围的下标,根据映射关系就可以找到物理内存空间了。

❗进程的地址空间的本质就是OS给进程画的大饼,告诉进程有这么多的空间范围可以使用。同时地址空间也需要被管理起来,先描述再组织。

❗每个进程都有自己的地址空间,地址空间是个结构体,结构体当中有各种代码区,数据区,各个区域(范围),各个区域的范围用来指定页表,(多个连续的地址,通过页表的映射到物理内存)

地址空间存在的意义

  • 将无序变成有序,让进程以统一的视角看待物理内存以及自己运行的各个区域。
  • 进程管理模块和内存管理模块进行解耦
  • 拦截非法请求

意义1

每个程序都有自己的代码和数据加载到物理内存(程序从磁盘加载到内存中,代码和数据不是线性存放的)。同时OS创建程序的进程控制块PCB。要求每个进程的PCB必须要记录程序的代码和数据再物理内存的位置。

  • ❗进程task_struct中记录程序的数据和代码在物理内存的位置比较有负担的
  • ❗越界访问,修改到其他进程的数据/代码情况都是存在的
  • ❗PCB记录位置是相对的,比较混乱的,OS对程序的数据/代码内存管理,调整非常不方便。
  • 不同的数据在物理内存的不同区域中。
  • 实际中的物理内存中,代码区,数据区,堆区,栈区,共享区,命令行参数和环境变量都是存在于不同的位置的。

对比增加地址空间/页表:

  • 将无序变成有序,让进程以统一的视角看待物理内存以及自己运行的各个区域。
  • 使用进程地址空间,统一,可以线性连续的访问各个区域的地址。
  • 进程地址空间的区域范围只有大小的区别。
  • 页表:虚拟地址到物理地址的映射就可以将无序变为有序。    

 

意义2

场景:

  • 假设程序的代码时2MB,现在已经执行了1MB了,还有1MB等待执行。
  • 此刻OS发现物理内存空间不够,把已经完成的1MB空间释放掉,
  • 把页表中关于部分的映射关系去掉,
  • 把已经执行的1MB的数据唤出到外设当中,使其处于挂起状态。

❗如果没有进程地址空间,那么进程当中就只有1MB的数据在物理内存中的地址信息。如果要再次使用也就没有了。

❗存在地址空间,进程地址空间仍然存在2MB的数据在物理内存中的地址信息。


  • OS在创建进程地址空间时,把数据和代码在物理内存中地址信息(虚拟地址)放到进程地址空间。
  • 申请一段堆/栈等空间(用start/end指针调整申请大小范围)
  • OS不着急申请物理内存,只有当真正的需要访问的时候才来申请物理内存,形成页表,映射关系。
  • 也就是说OS创建进程时也会开辟进程的数据/代码的虚拟地址空间,不会立刻开辟物理内存,只有当要使用物理内存的时候才会开辟。(❗提高内存的使用率)
  • 对进程进行的任何调度/管理各方面都可以稍后延迟管理/调度。
  • 如果在申请进程的地址空间的同时也申请物理内存,但没有使用。物理内存在OS层面上就相当于在没有使用的时间段被浪费了。

进程管理模块和内存管理模块进行解耦

通俗来讲,以进程的角度来申请内存只要认为地址空间的能申请即可,物理内存可等待......

意义3

  • 若进程可以直接访问物理内存的某个位置,不存在进程地址空间/页表映射这一步。若进程访问越界了,直接可能导致修改了物理内存中别的进程的数据。造成无法挽回的错误❌
  • 若存在进程物理空间/页表,访问越界,根据虚拟地址空间到页表中找不到对应的映射关系,页表和地址空间(这个查询过程)都是OS操作的。则会被OS拦截,根本不会到达物理内存。避免了无法挽回的错误✔(硬件)
  • OS会错误访问的请求直接拦截,不会涉及物理内存。防止进程向物理内存中写入错误数据,而是影响其他内存块。(拦截非法请求)

进程的地址空间不会100%的使用,只会使用地址空间一部分。空间在应用层确实存在,进程可能出现越界访问的情况(非法访问),若存在进程地址空间的存在,OS会直接拦截非法访问❌。所以的非法请求都不能通过地址空间到物理内存(保护物理内存)。

理解页表和写时拷贝

页表

  • CPU中的存在寄存器CR3:保存当前进程页表的虚拟地址。
  • CPU中的工作单位MMU(硬件)(Memory Management Unit 存储器管理单位)。
  • MMU:快速把CPU寄存器中的虚拟地址结合当前的进程的页表转化成物理地址。
  • MMU可以直接找到进程的页表。
  • 页表是非常复杂,后面我们也会慢慢讲解理解❗

  • 页表除了映射关系,还存在一些选项/字段/标记位。
  • 数据存在于物理内存当中,标记位为1;不在,为0。
  • 代码只能读,是r权限。
  • 数据只能读,是r权限;可读可写,是rw权限。

  • 物理内存严重不足,进程的代码和数据会被唤出内存,到磁盘交换分区Swap分区。进程处于挂起状态。
  • 此刻,进程的页表的标识位为0,表示不在物理内存中。
  • 权限为rw,可以读写。权限r,可以读。
  • 页表的虚拟地址保存,物理地址释放,物理内存空间也释放。虚拟地址空间仍然存在,进程仍然存在。
  • 系统/语法,同时这也是我们在Linux和windows写了一个崩溃的程序,并不影响其他进程的OS。因为这个程序的进程崩溃动作有任何向物理内存写入和影响,仅仅在地址空间和页表就被返回了(权限不允许)。如果可以写入,证明这个写入动作是合法的。

char *str = "hello world";

*str = 'H';

  • 为什么字符常量区不能被修改❓为什么只读❓
  • OS层面上保证的,字符常量在虚拟地址中通过页表映射到物理内存中,每个区域都是通过页表映射,字符常量在页表的映射关系标志位权限时只读。

写时拷贝

写时拷贝:是浅拷贝解决浅拷贝析构冲突的一种解决方案,写时拷贝也叫延时拷贝,几个对象共用一块空间,当执行读操作时不会有影响,当你需要进行写操作改变一个对象的内容时,空间的值不能被修改,会互相影响,那么就需要单独开辟一块空间将对象拷贝过去然后改,不改变就不需要开辟。

写时浅拷贝与深拷贝比较的优点:占用空间少(相同内容不开辟新空间),复制效率高。


malloc()等内存分配函数,在分配时只是建立了进程虚拟地址空间,并没有分配虚拟内存对应的物理内存。(虚拟地址是合法的)

当进程访问这些没有建立映射关系的虚拟内存时,处理器自动触发一个缺页异常

缺页中断:在请求分页系统中,可以通过查询页表中的状态位来确定所要访问的页面是否存在于内存中。每当所要访问的页面不在内存时,会产生一次缺页中断,此时操作系统会根据页表中的外存地址在外存中找到所缺的一页,将其调入内存。 

OS识别错误 

调度进程的时候,OS在地址空间/页表就识别到进程的错误有三种情况:

  • 是不是数据不在物理内存(挂起状态)(怎样判断:缺页中断)
  • 是不是数据需要写时拷贝(发生写时拷贝)(怎样判断后面讲:技术技术)
  • 如果都不是,才进行异常处理

怎么判断进程的是写时拷贝/还是非法访问❓ ☞后面讲

理解虚拟地址

  • 虚拟地址空间就是OS内核中的一个结构体,
  • 结构体当中包含了各种各样的区域/划分,
  • OS在构建加载进程时,也会进程对应的进程地址空间和页表。
  • 通过页表对非法请求进行拦截,同时也便于我们内存,
  • 我们就可以根据虚拟地址找到物理地址,好处以统一的视角看待内存,
  • 同时也会便于我们管理,提高内存的利用率。

❓在最开始的时候,地址空间/页表中的数据从哪里来(虚拟地址)

❓每个进程的代码区和数据区等的大小不一样,说明随着程序的不同,每个程序的模块区域都不一样大,都是要初始化虚拟地址从哪里来。

❓程序的编译成功形成的可执行程序.exe之后,函数名和变量名还存在吗。

不存在。全是地址。

  • 程序没有加载到内存当中的时候,全是二进制的时候,本身存在地址。
  • 对程序进行编制,这个地址就是虚拟地址(逻辑地址)
  • 在代码编译的时候,代码已经被编址过了,编址好了区域段,地址空间的内容都已经编址好了。0-4GB,天然的,线性的,平坦模式。(平坦模式编译)
  • 通俗来说,OS创建进程的地址空间时,在编译器编译程序阶段,就已经把地址空间的内容都准备好了,加载到内存中,直接使用即可。
  • 地址空间和页表的数据直接从程序读取来。
  • CPU根据物理地址找到虚拟地址,创建进程的地址空间和页表,从而让进程找到物理地址。
//命令行查看反汇编
objdump -S myprocess
objdump -S myprocess >test.c

fork解释

❓对于fork的返回值为什么返回两次

❓一个变量怎么可能既是 == 0 ,又是 > 0

  • 无论是子/父都要return,所以会return两次。
  • id原是父进程的变量
  • id本来是r权限(只能读)
  • 再fork创建子进程之后
  • id写入时,id为rw权限,发生写时拷贝
  • 则id存在两个值
  • return XXX return 的本质就是对id进行写入,发生写时拷贝❗
  • 代码用的同一个变量id,同一个变量在被编译器编译的时候,得到是同一个虚拟地址,fork创建子进程/页表/地址空间,子父指向同一个变量,当其中任何一方要return时,写入,发生写时拷贝,所以虚拟地址相同,物理内容不一样。id出现两个值的情况。
  1 #include<stdio.h>
  2 #include <sys/types.h>
  3 #include <unistd.h>
  4 
  5 int main()
  6 {
  7     pid_t id=getpid();
  8     pid_t parentid=getppid();
  9     printf("process is running,id=%d,parentid=%d\n",id,parentid);//只有父进程
 10     sleep(3);
 11     pid_t ret= fork();//创建子进程
 12     if(ret == -1)
 13     {
 14         return 1;
 15     }
 16     else if(ret == 0)
 17     {
 18         printf("I am child process!,pid=%d,ppid:%d\n",id,parentid);
 19         sleep(2);
 20     }                                                                          
 21     else//ret>0
 22     {
 23         printf("I am parent process!,pid=%d,ppid:%d\n",id,parentid);
 24         sleep(2);
 25     }
        return 0;
   }

🙂感谢大家的阅读,若有错误和不足,欢迎指正。下篇Linux2.6内核进程调度队列,来学个具体的例子。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分数求和(C语言)

一、N-S流程图&#xff1b; 二、运行结果&#xff1b; 三、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;double a 0, b 1, result1 2, sum 0;int i 0;//循环运算&#xff1b;for (i 1; i <…

人工智能大模型培训老师叶梓 探索知识库问答中的查询图生成:处理多跳复杂问题的新方法

在人工智能领域&#xff0c;基于知识库的问答&#xff08;KBQA&#xff09;技术正变得越来越重要。它使得机器能够理解自然语言问题&#xff0c;并从结构化的知识库中检索答案。然而&#xff0c;面对多跳复杂问题&#xff0c;传统的KBQA方法往往力不从心。近期&#xff0c;研究…

Threejs绘制传送带

接下来会做一个MES场景下的数字孪生&#xff0c;所以开始做车间相关的模型&#xff0c;不过还是尽量少用建模&#xff0c;纯代码实现&#xff0c;因为一方面可以动态使用&#xff0c;可以调节长度和宽度等&#xff0c; 下面这节就做一个简单的传送带&#xff0c;这是所有车间都…

C++心决之类和对象详解(中篇)(封装入门二阶)

目录 1.类的6个默认成员函数 2. 构造函数 2.1 概念 2.2 特性 3.析构函数 3.1 概念 3.2 特性 4. 拷贝构造函数 4.1 概念 4.2 特征 5.赋值运算符重载 5.1 运算符重载 5.2 赋值运算符重载 5.3 前置和后置重载 7.const成员 8.取地址及const取地址操作符重载 1.类的…

Win 进入桌面黑屏,只有鼠标

大家好&#xff0c;我叫秋意零。 今天&#xff0c;遇到一个同事电脑进入桌面黑屏&#xff0c;只有鼠标。经过询问沟通&#xff0c;说是 Windows 突然进行了自动更新&#xff0c;更新之后桌面就黑了屏。经过查询是一个桌面进程没启动才会导致桌面黑屏。首先分两种情况&#xff0…

【linux】软件工具安装 + vim 和 gcc 使用(上)

目录 1. linux 安装软件途径 2. rzsz 命令 3. vim 和 gcc 使用 a. vim的基本概念 b. 命令模式下的指令 c. 底行模式下的指令 1. linux 安装软件途径 源代码安装rpm安装 -- linux安装包yum安装&#xff08;最好&#xff0c;可以解决安装源&#xff0c;安装版本&#xff0…

ArrayList与顺序表(1)

前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; hellohello~&#xff0c;大家好&#x1f495;&#x1f495;&#xff0c;这里是E绵绵呀✋✋ &#xff0c;如果觉得这篇文章还不错的话还请点赞❤️❤️收藏&#x1f49e; &#x1f49e; 关注&#x1f4a5;&#x…

使用matplotlib的quiver绘制二维箭头图

使用ax.quiver绘制二维箭头图 1. matplotlib的quiver函数的调用方式 quiver函数是axes类的成员函数&#xff0c;其基本调用方式为&#xff1a; quiver([X, Y], U, V, [C], **kwargs) [X,Y]是箭头的位置&#xff0c;U,V是箭头的方向&#xff0c;C是箭头颜色。 具体而言&#x…

多项式轨迹规划

公众号“轻松玩转机器人”&#xff0c;欢迎关注。 1、简介 常用的多项式规划一般泛指3次、5次和7次等多项式规划&#xff0c;4次多项式规划用到的比较少&#xff0c;暂不介绍。 为什么奇数次多项式比较常用呢&#xff1f;因为其有偶数个系数&#xff01; 偶数个系数有什么用…

泛型的初步认识(1)

前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; hellohello~&#xff0c;大家好&#x1f495;&#x1f495;&#xff0c;这里是E绵绵呀✋✋ &#xff0c;如果觉得这篇文章还不错的话还请点赞❤️❤️收藏&#x1f49e; &#x1f49e; 关注&#x1f4a5;&#x…

Unity中的UI系统之UGUI

目录 概述UGUI基础——六大基础组件六大基础组件概述Canvas画布组件CanvasScaler画布缩放控制器组件必备知识恒定像素模式缩放模式恒定物理模式3D模式 Graphic Raycaster图形射线投射器EventSystem和Standalone Input ModuleRectTransform UGUI基础——三大基础控件Image图像控…

【解决】Caused by: javax.net.ssl.SSLHandshakeException: PKIX path building failed

问题原因&#xff1a; 在Java8及高版本以上的版本在源应用程序不信任目标应用程序的证书&#xff0c;因为在源应用程序的JVM信任库中找不到该证书或证书链。也就是目标站点启用了HTTPS 而缺少安全证书时出现的异常 解决方案&#xff1a; 我使用的是忽略证书验证 public clas…

vs code server for wsl closed unexpectedly

前言&#xff1a; 我的Windows 版本&#xff1a; 10.0.19045.4291 &#xff08;如果你是Win11或者你要使用WSL2请谨慎&#xff09; 之前是可以用的&#xff0c;但安装Vmware&#xff08;并安装了Ubuntu进行了一番实验后&#xff0c;就出现如标题所述问题&#xff09; 问题&a…

内存满了如何处理?

目录 虚拟内存 内存分配过程 直接内存回收和后台内存回收 回收内存的触发标准 那些内存被回收呢? 内存回收后,内存还是不够怎么办呢? 虚拟内存 介绍操作系统内存如何使用时,不可以避免的先认识到虚拟内存 首先我们通过虚拟内存的作用,来认识一下: 1.虚拟内存可以使得…

基础SQL DML-插入语句

插入语句前&#xff0c;我们先创建一个表。表的创建在DDL语句里面涉及&#xff0c;可以参考&#xff1a;小赖同学吖-CSDN博客 我们创建一个员工表进行数据的插入操作 插入&#xff08;添加&#xff09;语句的语法 给员工表添加一条记录 给员工表添加多条记录 也可以通过下面的方…

Python 面向对象——2.类与对象实例属性补充解释,self的作用等

本章学习链接如下&#xff1a; Python 面向对象——1.基本概念 实例的属性 1.创建对象 在上一小节的学习中我们提到了类中的变量与函数变量的区别&#xff0c;self.param1和param1&#xff0c;接下来我们继续详细解释这个知识点。 当我们创建一个学生的类&#xff0c;比如…

铜缆与网线:数字时代的信息高速公路

在现代社会&#xff0c;信息传输已成为日常生活的重要部分。从个人通信到全球数据中心&#xff0c;铜缆和网线扮演着至关重要的角色。本文将详细介绍铜缆和网线的类型、特点以及它们在数字时代的应用。 铜缆的种类与应用 铜缆的类型 UTP&#xff08;无屏蔽双绞线&#xff09;&…

登录的几种方式

一、session 1、客户端发送请求&#xff0c;服务器将登录信息存储在 Session 中&#xff0c;Session 依赖于 Cookie&#xff08;cookie指的就是在浏览器里面存储的一种数据&#xff0c;仅仅是浏览器实现的一种数据存储功能。Cookie实际上是一小段的文本信息。&#xff09;&…

Arthas介绍及使用技巧

文章目录 简介能做什么&#xff1f; 使用下载并启动arthas选择应用 java 进程退出 arthas 常用查看命令帮助查看 dashboard通过 thread 命令来获取到线程的栈通过 jad 来反编译 Classwatch 查看方法出入参、sc 搜索类: 查看已加载类所在的包monitor 方法执行监控trace 方法内调…

牛客NC238 加起来和为目标值的组合【中等 DFS C++、Java、Go、PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/172e6420abf84c11840ed6b36a48f8cd 思路 本题是组合问题&#xff0c;相同元素不同排列仍然看作一个结果。 穷经所有的可能子集&#xff0c;若和等于target&#xff0c;加入最终结果集合。 给nums排序是为了方便…