Linux之内存管理前世今生(一)

一个程序(如王者荣耀)平常是存储在硬盘上的,运行时才把这个程序载入内存,CPU才能执行。

问题:
这个程序载入内存的哪个位置呢?载入内核所在的空间吗?系统直接挂了。

一、虚拟内存

1.1 内存分段管理

最早的程序员写代码时是需要指定程序在内存的运行位置的,也即他们使用绝对地址来进行内存访问的。

  • 计算机刚启动时采用实模式运行,即使用绝对地址来进行内存访问;
  • 操作系统加载完成会变成保护模式,即使用虚拟地址进行内存访问。

问题来了:

  • 在有限的物理内存空间内,多道程序是无法并行运行的,举个栗子:王者荣耀需要在内存地址0x0000 ~ 0x6000上,微信需要在内存地址 0x0000 ~ 0x3000,这下好了,谁也跑不起来,直接崩了;
  • 程序员需要关注自己写的程序要跑在多大的物理内存上,内存地址怎么分配……这耦合性也太高了,而且不安全,容易访问到其他程序已使用的物理内存。

解决方法很简单:所有程序无需关心内存物理地址,代码经过编译,链接看到的地址都一样,从 0 开始到最大地址的空间,这个地址空间是独立的,是该程序私有的,其它程序既看不到,也不能访问该地址空间,这个地址空间和其它程序无关,记为虚拟内存(Virtual memory) ,虚拟内存地址记为虚拟地址,物理内存的地址记为物理地址

  • 程序运行时找一块空闲物理内存装入即可。比如之前例子:

    • 王者荣耀(虚拟地址0x0000 ~ 0x6000)运行在物理内存0x0000 ~ 0x6000;
    • 微信(虚拟地址0x0000 ~ 0x3000)运行在0x6000 ~ 0x9000;
  • 上述分配意味着我们需要记录虚拟空间和物理空间的映射关系:我们将这个表记为段表,因为映射的一段完整连续的物理内存。

    进程虚拟地址物理地址
    王者荣耀0x0000 ~ 0x60000x0000 ~ 0x6000
    微信0x0000 ~ 0x30000x6000 ~ 0x9000
  • 段表实际记录的是段的基址和边界,使用时通过段表找到物理内存地址,再结合段内偏移量即可定位数据。

在这里插入图片描述

上述为内存分段管理大致原理,实际内存分段管理和早期硬件发展相关,分为几个大的段,同时配合段寄存器使用。

cs: 代码段
ds: 数据段
ss: 栈段
es:扩展段

1.2 内存分页管理

前面我们成功的让2个程序同时正确的跑起来了,但是物理内存是有限的,如之前所示最大地址为0xA000,现在想听网易云音乐(虚拟地址为 0x0000 ~ 0x4000),这就尴尬了,空闲的物理内存(0x9000 ~ 0xA000)不够支持网易云音乐了,但现实中,我们上述需求的确可以同时满足啊?

  • 上述无法使用的内存空间(0x9000 ~ 0xA000),称为内存外部碎片;即分段管理存在明显的外部碎片;
  • 相对应的,已分配过程序内存内部存在没有使用到的空间称为内存内部碎片

其实也很简单,我们把程序分批次装入物理内存中,每次只载入一部分来满足运行即可,我们把这部分程序内容记为 page (页),存放页的物理内存区域记为page frame (页框)

  • 管理内存的最小单位是页;
  • 分页管理没有外部碎片了,每一个页框均可使用。

实际当中每次载入多少数据合适呢?一般为 4KB,即 页的大小为4KB,页框的大小也为 4KB。将物理内存和虚拟内存按照的大小(4KB) 分割,需要哪页就加载那页内容,找一个空闲的页框存放即可。这样我们在有限的物理内存内,载入并正确并行了多道程序。

在这里插入图片描述

按照上述策略,我们成功得将3个程序并行跑起来了,如下图所示:

在这里插入图片描述

1.2.1 页表

虚拟内存页和物理内存页的映射关系也得占用物理内存进行存储,即上图所示的 页表,一个页表也占用一个页框存储,即一个页表最大为 4KB。下图即为页表图示,观察下图,虚拟页号就是下标,所以页表可以用一个一维数组存储即可:数组下标为虚拟页号,数组内容为物理页号和物理内存起始地址等其它额外信息:

在这里插入图片描述

页面大小为4kByte,因此每个页框的低12位均为0。内核将低12位充分利用,每个位都表示对应虚拟页的相关属性。同样的骚操作在64位JVM中指针压缩也有。

1.2.2 物理地址计算

给定虚拟地址 0x0809 怎么计算物理地址呢?

  1. 根据虚拟地址和页大小(4KB,按Byte编址需要 12 个 bit )计算虚拟页号,和页内偏移量;
    • 虚拟页号: 0(0x0809 / 4k );
    • 页内偏移量: 9(0x0800 % 4k);
  2. 页表基址寄存器找到页表物理地址;
  3. 查页表找到物理页号;
  4. 根据物理页号起始地址和页内偏移量得到物理地址。
    在这里插入图片描述

计算机中完成上述地址转换的部件由特定硬件完成,叫做 MMU(Memory Management Unit,内存管理单元),它位于CPU 芯片中。

1.2.3 缺页中断

因为我们只将程序部分页面载入到内存当中,当运行完这些页面继续往后运行时,进程访问的虚拟地址在页表中查不到时,即后续页面还在磁盘上,此时 MMU 会触发缺页中断(page fault),从磁盘上将对应页面加载到物理内存中空闲页框内,同时更新页表。

1.2.4 多级页表

在 32 位的环境下,每个进程的虚拟内存为4GB,一个页表最大为4KB,页内偏移量12 bit,剩余 20bit (32-12) 用于页号,可以表示约 1,000,000(220)页, 页表中每一项(记为页表项)需要 4Byte,所以4GB空间需要4MB(220 * 4Byte)物理内存来存储页表。

这意味着,如果计算机并行10个进程的话需要 40MB 内存,实际中开启的进程远不止10个,需要更大内存来存储页表。

所以不可能将页表(4MB)全部放进内存,又必须能看到页表全貌,怎么办?
兵仙韩信说,只需十个将军,即可统帅百万大军;
同理,只需一个页表索引(页目录),即可查找百万页表项,即:
一页可以放1024(4KB / 4Byte)个页索引,二级可以存储即可表示 220(1024 * 1024)页,刚好覆盖整个 4GB (4KB * 220 )虚拟地址空间。

在这里插入图片描述

  • 使用时通过一级页表(记为页目录),找到二级页表,从而得到最终物理地址;
  • 若二级页表不存在,触发缺页中断进行加载即可;
  • 这不是一颗B树吗?树高2。
1.2.4.1 多级页表物理地址
  • 在 32 位的环境下,我们将虚拟地址原页号进行分割,10bit 存放一级页号(页目录),10bit 存放二级页号(页表项),如下图所示:
  • 在 64 位的环境下,二级分页是无法满足的,实际使用时分为四级索引,即B树高度为4;
    • 四级索引前面3级为页目录,叶子节点为页表,依次命名为:
      • 全局页目录项 PGD(Page Global Directory)
      • 上层页目录项 PUD(Page Upper Directory)
      • 中间页目录项 PMD(Page Middle Directory)
      • 页表项 PTE(Page Table Entry)
    • 页表项为 8Byte,一页(4KB)可以索引 512 (4KB / 8Byte) 个页;
    • 四级可以索引 5124 个页,可以表示 256TB (4KB * 5124) 虚拟空间;
    • 512 个页号使用 9 (29 = 512)个 bit就可以表示;
    • 四级页表使用 36(4 * 9)个bit可以表示;
    • 64 位环境用 48(36 + 12)个bit 进行寻址(意味着PGD中 高位 16 bit 全0或者全1),表示 256TB 空间

在这里插入图片描述

小节:虚拟地址本质上是索引+偏移量

1.2.5 页表缓存TLB(Translation Lookaside Buffer)

计算机中为协调CPU和内存的访问速度(一次内存的访问,大约需要 120 个 CPU Cycle),中间加了 高速缓存(CPU Cache)

  • 高速缓存使用特定的由 SRAM (静态RAM (random-access memory,随机访问存储器)) 组成的物理芯片,内存使用DRAM(动态RAM);
  • 高速缓存为3级:L1/L2/L3 Cache。其中 L1/L2 是 CPU 私有,L3 是所有 CPU 共享。
  • 缓存行(Cache Line):高速缓存的最小单元,一次从内存中读取的数据大小。常用的 Intel 服务器 Cache Line 的大小通常是 64 字节。
  • CPU Cache 的命中率通常能达到 95% 以上。

我们前面提到的 页表项(PTE) 同样需要从内存加载到高速缓存来提高CPU访问速度,由于高速缓存空间远小于内存空间,所以只能缓存程序中最常访问的页表项,我们把缓存页表项的这块区域称为转址旁路缓存(TLB,Translation Lookaside Buffer),也称为快表(内存中的表相应为慢表)。

在这里插入图片描述

1.3 内存段页式管理

内存管理最开始分段管理,后面又提出分页管理,由于硬件和版本限制,现在操作系统中的内存管理理论上是由段页结合一起管理的,即先分段,然后段内分页。
实际使用时,操作系统做了简化,将代码段和数据段的基址设为0,段空间大小为这个虚拟内存的大小。
即现代操作系统中,分段管理名存实亡。

在这里插入图片描述

在 CPU 中,程序使用逻辑内存地址;

1.4 虚拟内存空间整体布局

虚拟内存空间并非全部留给程序,一般而言高地址段为内核空间,用于系统内核使用,低地址段为用户空间,留给程序使用,如下图所示:

在这里插入图片描述

1.4.1 用户态虚拟内存

程序是由源代码经过编译、链接形成的可执行文件(ELF)。ELF中分为代码段(.text)、数据段(.data)、未初始化数据段(.bss)等很多逻辑段,虚拟内存为保持这种程序的逻辑性,同样也在逻辑上将内存划分为代码段、数据段、堆、文件映射与匿名映射区、栈等。

在这里插入图片描述

注意

  • 一个段(segment)一般包含多个页(page),每个段的长度并不相等;
  • 保留区这段虚拟内存是不可访问的,因为在大多数操作系统中,数值比较小的地址通常被认为不是一个合法的地址,这块小地址是不允许访问的。比如在 C 语言中我们通常会将一些无效的指针设置为 NULL,指向这块不允许访问的地址。
  • 文件映射区使用时均是从高地址向低地址分配
  • 使用时从低地址向高地址分配
  • 不可访问段仅存在于64位环境中,为了防止程序在读写数据段的时候越界访问到代码段,这个保护段可以让越界访问行为直接崩溃,防止它继续往下运行。
  • 通过 cat /proc/pid/maps 或者 pmap pid 查看进程的虚拟内存空间布局以及其中包含的所有内存区域。
  • 进程进入内核态之后使用的仍然是虚拟内存地址,只不过在内核中使用的虚拟内存地址被限制在了内核态虚拟内存空间范围中。

虚拟内存的内容暂时介绍到这里,后续会介绍物理内存的实际分配情况。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java基于SSM框架的互助学习平台小程序【附源码、文档】

博主介绍:✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇&#x1f3…

【Rust自学】16.3. 共享状态的并发

喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 16.3.1. 使用共享来实现并发 还记得Go语言有一句名言是这么说的:Do not commun…

Python 数据分析 - Matplotlib 绘图

Python 数据分析 - Matplotlib 绘图 简介绘图折线图单线多线子图 散点图直方图条形图纵置横置多条 饼图 简介 Matplotlib 是 Python 提供的一个绘图库,通过该库我们可以很容易的绘制出折线图、直方图、散点图、饼图等丰富的统计图,安装使用 pip install…

Java进阶(二):Java设计模式

目录 设计模式 一.建模语言 二.类之间的关系 1.依赖关系 2.关联关系 3.聚合关系 4.组合关系 5.继承关系 6.实现关系 三.面向对象设计原则 单一职责原则 开闭原则 里氏替换原则 依赖倒置 接口隔离原则 迪米特原则 组合/聚合(关联关系)复用原则 四.23种设计模式…

双层Git管理项目,github托管显示正常

双层Git管理项目,github托管显示正常 背景 在写React项目时,使用Next.js,该项目默认由git托管。但是我有在项目代码外层记笔记的习惯,我就在外层使用了git托管。 目录如下 code 层内也有.git 文件,对其托管。 我没太在意&…

群晖docker获取私有化镜像http: server gave HTTP response to HTTPS client].

群晖docker获取私有化镜像提示http: server gave HTTP response to HTTPS clien 问题描述 层级时间用户事件Information2023/07/08 12:47:45cxlogeAdd image from xx.xx.31.240:1923/go-gitea/gitea:1.19.3Error2023/07/08 12:47:48cxlogeFailed to pull image [Get "http…

机器学习:支持向量机

支持向量机(Support Vector Machine)是一种二类分类模型,其基本模型定义为特征空间上的间隔最大的广义线性分类器,其学习策略便是间隔最大化,最终可转化为一个凸二次规划问题的求解。 假设两类数据可以被 H x : w T x…

相互作用感知的蛋白-小分子对接模型 - Interformer 评测

Interformer 是一个应用于分子对接和亲和力预测的深度学习模型,基于 Graph-Transdormer 架构的模型,利用相互作用(氢键、疏水)感知的混合密度网络(interaction-aware mixture den sity network, MDN&#x…

如果我想设计一款复古风格的壁纸,应该选什么颜色?

设计复古风格的壁纸时,选择合适的颜色是营造怀旧和经典氛围的关键。复古风格通常使用一些温暖、柔和且带有岁月痕迹的色调。以下是一些适合复古风格壁纸的颜色选择和搭配建议: 一、复古风格的主色调 棕色系: 特点:棕色是复古风格的…

AI 浪潮席卷中国年,开启科技新春新纪元

在这博主提前祝大家蛇年快乐呀!!! 随着人工智能(AI)技术的飞速发展,其影响力已经渗透到社会生活的方方面面。在中国传统节日 —— 春节期间,AI 技术也展现出了巨大的潜力,为中国年带…

WPS数据分析000007

目录 一、分列 智能分列 出生日期 数值转换 公式不运算 二、数据对比 离职员工 新入职员工 都在职的员工 三、合并计算 四、拆分表格 合并表格 一、分列 智能分列 出生日期 数据求和 文本型数字左对齐;数值型数字右对齐 数值转换 方式一: 方…

fps一些内容添加

1 增强输入要点记录 输入 :输入值的类型 布尔 1d,2d,3d 映射:就是确定按键输入键位,输入类型,和一些触发器(按键方式)修改器(对输出值进行修改) 基本的&am…

深入探讨数据库索引类型:B-tree、Hash、GIN与GiST的对比与应用

title: 深入探讨数据库索引类型:B-tree、Hash、GIN与GiST的对比与应用 date: 2025/1/26 updated: 2025/1/26 author: cmdragon excerpt: 在现代数据库管理系统中,索引技术是提高查询性能的重要手段。当数据量不断增长时,如何快速、有效地访问这些数据成为了数据库设计的核…

【反悔堆】【hard】力扣871. 最低加油次数

汽车从起点出发驶向目的地,该目的地位于出发位置东面 target 英里处。 沿途有加油站,用数组 stations 表示。其中 stations[i] [positioni, fueli] 表示第 i 个加油站位于出发位置东面 positioni 英里处,并且有 fueli 升汽油。 假设汽车油…

知识库建设对提升团队协作与创新能力的影响分析

内容概要 在当今快速变革的商业环境中,知识库建设的重要性愈发凸显。它不仅是信息存储的载体,更是推动组织内部沟通与协作的基石。通过系统整理与管理企业知识,团队成员能够便捷地访问相关信息,使得协作过程更为流畅,…

SpringBoot-Vue整合百度地图

文章目录 一、Spring Boot整合百度地图的步骤1. 申请百度地图的AK值2. 创建实体类3. 创建Controller层4. 前端集成百度地图4.1 在Vue项目中安装百度地图Vue组件库4.2 在Vue项目中引入百度地图API4.3 创建地图组件 二、实现功能说明1. 前端部分:2. 后端部分&#xff…

【Docker】快速部署 Nacos 注册中心

【Docker】快速部署 Nacos 注册中心 引言 Nacos 注册中心是一个用于服务发现和配置管理的开源项目。提供了动态服务发现、服务健康检查、动态配置管理和服务管理等功能,帮助开发者更轻松地构建微服务架构。 步骤 拉取镜像 docker pull nacos/nacos-server启动容器…

RAG技术:通过向量检索增强模型理解与生成能力

网罗开发 (小红书、快手、视频号同名) 大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等…

Java设计模式:行为型模式→策略模式

Java 策略模式详解 1. 定义 策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列的算法,将每一个算法封装起来,并使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户。通过这种模式&#xf…

linux通过deb包安装(命令模式)

通过下载deb包安装Chrome浏览器 - lyy19s Wikihttps://lyy1119.github.io/%E8%BD%AF%E4%BB%B6%E4%BD%BF%E7%94%A8/Linux/InstallChrome/