一文解释Linux的内存分页管理

内存是计算机的主存储器。内存为进程开辟出进程空间,让进程在其中保存数据。我将从内存的物理特性出发,深入到内存管理的细节,特别是了解虚拟内存和内存分页的概念。

内存

简单地说,内存就是一个数据货架。内存有一个最小的存储单位,大多数都是一个字节。内存用内存地址(memory address)来为每个字节的数据顺序编号。因此,内存地址说明了数据在内存中的位置。内存地址从0开始,每次增加1。这种线性增加的存储器地址称为线性地址(linear address)。为了方便,我们用十六进制数来表示内存地址,比如0x00000003、0x1A010CB0。这里的“0x”用来表示十六进制。“0x”后面跟着的,就是作为内存地址的十六进制数。

内存地址的编号有上限。地址空间的范围和地址总线(address bus)的位数直接相关。CPU通过地址总线来向内存说明想要存取数据的地址。以英特尔32位的80386型CPU为例,这款CPU有32个针脚可以传输地址信息。每个针脚对应了一位。如果针脚上是高电压,那么这一位是1。如果是低电压,那么这一位是0。32位的电压高低信息通过地址总线传到内存的32个针脚,内存就能把电压高低信息转换成32位的二进制数,从而知道CPU想要的是哪个位置的数据。用十六进制表示,32位地址空间就是从0x00000000 到0xFFFFFFFF。

内存的存储单元采用了随机读取存储器(RAM, Random Access Memory)。所谓的“随机读取”,是指存储器的读取时间和数据所在位置无关。与之相对,很多存储器的读取时间和数据所在位置有关。就拿磁带来说,我们想听其中的一首歌,必须转动带子。如果那首歌是第一首,那么立即就可以播放。如果那首歌恰巧是最后一首,我们快进到可以播放的位置就需要花很长时间。我们已经知道,进程需要调用内存中不同位置的数据。如果数据读取时间和位置相关的话,计算机就很难把控进程的运行时间。因此,随机读取的特性是内存成为主存储器的关键因素。

内存提供的存储空间,除了能满足内核的运行需求,还通常能支持运行中的进程。即使进程所需空间超过内存空间,内存空间也可以通过少量拓展来弥补。换句话说,内存的存储能力,和计算机运行状态的数据总量相当。内存的缺点是不能持久地保存数据。一旦断电,内存中的数据就会消失。因此,计算机即使有了内存这样一个主存储器,还是需要硬盘这样的外部存储器来提供持久的储存空间。

虚拟内存

内存的一项主要任务,就是存储进程的相关数据。我们之前已经看到过进程空间的程序段、全局数据、栈和堆,以及这些这些存储结构在进程运行中所起到的关键作用。有趣的是,尽管进程和内存的关系如此紧密,但进程并不能直接访问内存。在Linux下,进程不能直接读写内存中地址为0x1位置的数据。进程中能访问的地址,只能是虚拟内存地址(virtual memory address)。操作系统会把虚拟内存地址翻译成真实的内存地址。这种内存管理方式,称为虚拟内存(virtual memory)。

每个进程都有自己的一套虚拟内存地址,用来给自己的进程空间编号。进程空间的数据同样以字节为单位,依次增加。从功能上说,虚拟内存地址和物理内存地址类似,都是为数据提供位置索引。进程的虚拟内存地址相互独立。因此,两个进程空间可以有相同的虚拟内存地址,如0x10001000。虚拟内存地址和物理内存地址又有一定的对应关系,如图1所示。对进程某个虚拟内存地址的操作,会被CPU翻译成对某个具体内存地址的操作。

图1 虚拟内存地址和物理内存地址的对应

应用程序来说对物理内存地址一无所知。它只可能通过虚拟内存地址来进行数据读写。程序中表达的内存地址,也都是虚拟内存地址。进程对虚拟内存地址的操作,会被操作系统翻译成对某个物理内存地址的操作。由于翻译的过程由操作系统全权负责,所以应用程序可以在全过程中对物理内存地址一无所知。因此,C程序中表达的内存地址,都是虚拟内存地址。比如在C语言中,可以用下面指令来打印变量地址:

int v = 0;
printf("%p", (void*)&v);

本质上说,虚拟内存地址剥夺了应用程序自由访问物理内存地址的权利。进程对物理内存的访问,必须经过操作系统的审查。因此,掌握着内存对应关系的操作系统,也掌握了应用程序访问内存的闸门。借助虚拟内存地址,操作系统可以保障进程空间的独立性。只要操作系统把两个进程的进程空间对应到不同的内存区域,就让两个进程空间成为“老死不相往来”的两个小王国。两个进程就不可能相互篡改对方的数据,进程出错的可能性就大为减少。

另一方面,有了虚拟内存地址,内存共享也变得简单。操作系统可以把同一物理内存区域对应到多个进程空间。这样,不需要任何的数据复制,多个进程就可以看到相同的数据。内核和共享库的映射,就是通过这种方式进行的。每个进程空间中,最初一部分的虚拟内存地址,都对应到物理内存中预留给内核的空间。这样,所有的进程就可以共享同一套内核数据。共享库的情况也是类似。对于任何一个共享库,计算机只需要往物理内存中加载一次,就可以通过操纵对应关系,来让多个进程共同使用。IPO中的共享内存,也有赖于虚拟内存地址。

内存分页

虚拟内存地址和物理内存地址的分离,给进程带来便利性和安全性。但虚拟内存地址和物理内存地址的翻译,又会额外耗费计算机资源。在多任务的现代计算机中,虚拟内存地址已经成为必备的设计。那么,操作系统必须要考虑清楚,如何能高效地翻译虚拟内存地址。

记录对应关系最简单的办法,就是把对应关系记录在一张表中。为了让翻译速度足够地快,这个表必须加载在内存中。不过,这种记录方式惊人地浪费。如果树莓派1GB物理内存的每个字节都有一个对应记录的话,那么光是对应关系就要远远超过内存的空间。由于对应关系的条目众多,搜索到一个对应关系所需的时间也很长。这样的话,会让树莓派陷入瘫痪。

因此,Linux采用了分页(paging)的方式来记录对应关系。所谓的分页,就是以更大尺寸的单位页(page)来管理内存。在Linux中,通常每页大小为4KB。如果想要获取当前树莓派的内存页大小,可以使用命令:

$getconf PAGE_SIZE

得到结果,即内存分页的字节数:

4096

返回的4096代表每个内存页可以存放4096个字节,即4KB。Linux把物理内存和进程空间都分割成页。

内存分页,可以极大地减少所要记录的内存对应关系。我们已经看到,以字节为单位的对应记录实在太多。如果把物理内存和进程空间的地址都分成页,内核只需要记录页的对应关系,相关的工作量就会大为减少。由于每页的大小是每个字节的4000倍。因此,内存中的总页数只是总字节数的四千分之一。对应关系也缩减为原始策略的四千分之一。分页让虚拟内存地址的设计有了实现的可能。

无论是虚拟页,还是物理页,一页之内的地址都是连续的。这样的话,一个虚拟页和一个物理页对应起来,页内的数据就可以按顺序一一对应。这意味着,虚拟内存地址和物理内存地址的末尾部分应该完全相同。大多数情况下,每一页有4096个字节。由于4096是2的12次方,所以地址最后12位的对应关系天然成立。我们把地址的这一部分称为偏移量(offset)。偏移量实际上表达了该字节在页内的位置。地址的前一部分则是页编号。操作系统只需要记录页编号的对应关系。


图2 地址翻译过程

多级分页表

内存分页制度的关键,在于管理进程空间页和物理页的对应关系。操作系统把对应关系记录在分页表(page table)中。这种对应关系让上层的抽象内存和下层的物理内存分离,从而让Linux能灵活地进行内存管理。由于每个进程会有一套虚拟内存地址,那么每个进程都会有一个分页表。为了保证查询速度,分页表也会保存在内存中。分页表有很多种实现方式,最简单的一种分页表就是把所有的对应关系记录到同一个线性列表中,即如图2中的“对应关系”部分所示。

这种单一的连续分页表,需要给每一个虚拟页预留一条记录的位置。但对于任何一个应用进程,其进程空间真正用到的地址都相当有限。我们还记得,进程空间会有栈和堆。进程空间为栈和堆的增长预留了地址,但栈和堆很少会占满进程空间。这意味着,如果使用连续分页表,很多条目都没有真正用到。因此,Linux中的分页表,采用了多层的数据结构。多层的分页表能够减少所需的空间。

我们来看一个简化的分页设计,用以说明Linux的多层分页表。我们把地址分为了页编号和偏移量两部分,用单层的分页表记录页编号部分的对应关系。对于多层分页表来说,会进一步分割页编号为两个或更多的部分,然后用两层或更多层的分页表来记录其对应关系,如图3所示。


图3 多层分页表


在图3的例子中,页编号分成了两级。第一级对应了前8位页编号,用2个十六进制数字表示。第二级对应了后12位页编号,用3个十六进制编号。二级表记录有对应的物理页,即保存了真正的分页记录。二级表有很多张,每个二级表分页记录对应的虚拟地址前8位都相同。比如二级表0x00,里面记录的前8位都是0x00。翻译地址的过程要跨越两级。我们先取地址的前8位,在一级表中找到对应记录。该记录会告诉我们,目标二级表在内存中的位置。我们再在二级表中,通过虚拟地址的后12位,找到分页记录,从而最终找到物理地址。

多层分页表就好像把完整的电话号码分成区号。我们把同一地区的电话号码以及对应的人名记录同通一个小本子上。再用一个上级本子记录区号和各个小本子的对应关系。如果某个区号没有使用,那么我们只需要在上级本子上把该区号标记为空。同样,一级分页表中0x01记录为空,说明了以0x01开头的虚拟地址段没有使用,相应的二级表就不需要存在。正是通过这一手段,多层分页表占据的空间要比单层分页表少了很多。
多层分页表还有另一个优势。单层分页表必须存在于连续的内存空间。而多层分页表的二级表,可以散步于内存的不同位置。这样的话,操作系统就可以利用零碎空间来存储分页表。还需要注意的是,这里简化了多层分页表的很多细节。最新Linux系统中的分页表多达3层,管理的内存地址也比本章介绍的长很多。不过,多层分页表的基本原理都是相同。

综上,我们了解了内存以页为单位的管理方式。在分页的基础上,虚拟内存和物理内存实现了分离,从而让内核深度参与和监督内存分配。应用进程的安全性和稳定性因此大为提高。

技术前沿拓展

前端开发,你的认知不能仅局限于技术内,需要发散思维了解技术圈的前沿知识。细心的人会发现,开发内部工具的过程中,大量的页面、场景、组件等在不断重复,这种重复造轮子的工作,浪费工程师的大量时间。

介绍一款程序员都应该知道的软件JNPF 快速开发平台,很多人都尝试用过它,它是功能的集大成者,任何信息化系统都可以基于它开发出来。

这是一个基于 Java Boot/.Net Core 构建的简单、跨平台快速开发框架。前后端封装了上千个常用类,方便扩展;集成了代码生成器,支持前后端业务代码生成,实现快速开发,提升工作效率;框架集成了表单、报表、图表、大屏等各种常用的 Demo 方便直接使用;后端框架支持 Vue2、Vue3。如果你有闲暇时间,可以做个知识拓展。

看完本文如果觉得有用,记得点个赞支持,收藏起来说不定哪天就用上啦~

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

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

相关文章

书客、孩视宝、明基护眼台灯好不好用?护眼灯测评对比!

现在一些家长对自家孩子的健康也是特别的重视,从小时开始的儿童枕;再到保护眼睛的护眼台灯、OK眼镜;再到青少年时期的生长激素...可以说是穷出不尽,但是关于孩子使用的东西又不能马虎,每次要帮孩子选东西的时候可是一阵…

四川思维跳动商务信息咨询有限公司电商服务怎么样

随着电商行业的迅猛发展,越来越多的企业开始寻求专业的电商服务以提升自身竞争力。四川思维跳动商务信息咨询有限公司作为一家专注于电商服务的公司,凭借其卓越的服务品质和创新能力,已经在业内树立了良好的口碑。本文将为您详细解析四川思维…

办公宝典:兼具多功能的4大办公软件app推荐!

现在,随着全球化和互联网的发展,团队协作成为企业成功的关键所在。在这个环境中,协同办公软件成为无法离开的工具。 在市面上,有很多种协同办公软件可供选择。这里介绍4款与协同办公密切相关的软件,它们提供了许多功能…

C#基础:字段的初始化、继承

一、字段的初始化 class Test {static int Main(string[] args){var list new List<Calculate>();//1.Calculate中加入abvar calculate new Calculate { a 2, b 3 };//2.Calculate中加入Value列表calculate.Values.Add(new Value { id 1, value 6 });calculate.Va…

【操作系统】输入输出系统

6.1 I/O系统的功能、模型和接口 I/O系统管理的主要对象是I/O设备和相应的设备控制器。其最主要的任务是&#xff0c;完成用户提出的I/O请求&#xff0c;提高I/O速率&#xff0c;以及提高设备的利用率&#xff0c;并能为更高层的进程方便地使用这些设备提供手段。 6.1.1 I/O系…

Ubuntu 22.04/20.04 安装 SSH

OpenSSH 是安全远程通信的重要工具&#xff0c;提供了一种安全的方式来访问和管理服务器。对于那些计划在 Ubuntu 22.04 Jammy Jellyfish 或其较旧的稳定版本的 Ubuntu 20.04 Focal Fossa 上安装 SSH 并启用它的人来说&#xff0c;了解其功能和优势至关重要。 OpenSSH的主要特…

技术学习|CDA level I 描述性统计分析(数据的描述性统计分析)

技术学习|CDA level I 描述性统计分析&#xff08;数据的描述性统计分析&#xff09; 数据的描述性统计分析常从数据的集中趋势、离散程度和分布形态3个方面进行。 一、集中趋势 集中趋势是指数据向其中心值靠拢的趋势。测量数据的集中趋势&#xff0c;主要是寻找其中心值。…

修改选择框el-select样式,显示及下拉样式

修改选择框el-select样式,显示及下拉样式 .el-input__inner {background: rgba(25, 126, 195, 0.2);border: none;color: #fff; }.el-select-dropdown {background: rgba(19, 73, 104, 0.79);border: 2px solid #48e3ff;border-radius: 0; }.el-popper .popper__arrow {display…

VUE局部组件

实现局部组件的注册功能&#xff0c;样例如下 代码如下 <html><head><meta charset"utf-8"><title></title></head><body><div id"app"><hello-word></hello-word><hello-tom></he…

多个微信的朋友圈如何高效管理?

大家都知道&#xff0c;在当今社交媒体盛行的时代&#xff0c;微信朋友圈已成为了我们交流和分享生活的重要平台。但是&#xff0c;对于那些同时管理多个微信号的人来说&#xff0c;如何高效省时地管理这些账号的朋友圈就成了一项挑战。 今天我将分享一个神奇的微信管理工具&a…

内存的基础知识-第四十天

目录 什么是内存&#xff1f;内存的作用 常用的数量单位 指令的工作原理 思考 三种装入方式 绝对装入 可重定位装入&#xff08;静态重定位&#xff09; 动态运行时装入&#xff08;动态重定位&#xff09; 写程序到程序运行 编译和链接 链接的三种方式 本节思维导…

达梦数据:数字化时代,国产数据库第一股终于到来?

又是新的一年开始。回首一年前的此时&#xff0c;在大家千呼万唤地期待中&#xff0c;数据基础制度体系的纲领性文件正式发布。 时隔一年之后&#xff0c;数据资源入表如约而至。2024年1月1日《企业数据资源相关会计处理暂行规定》正式施行&#xff0c;各行各业海量数据巨大的…

Fiddler工具 — 6.Fiddler页面布局之工具栏

Fiddler工具栏就是平时比较常用功能的一个快捷方式。 下面一一说明&#xff1a; 1、第一组工具&#xff1a; WinConfig&#xff1a;可以在里面配置一些windows系统的应用程序&#xff0c;配置在里面的应用的请求Fiddler都可以抓到&#xff0c;Fiddler5新增功能。 换句话说&…

caj转换成pdf有哪些方法?

caj转换成pdf有哪些方法&#xff1f;PDF是一个被广泛支持的文件格式&#xff0c;这种格式基本上在所有的操作系统和设备上都是支持使用的&#xff0c;也能够将PDF文件打开和查看的&#xff0c;相比于caj文件&#xff0c;它就只能通过一下特定的软件或者是插件才能够将caj打开或…

潮玩宇宙大逃杀游戏搭建

潮玩宇宙是当下较火的社交互动平台&#xff0c;它不仅涵盖了各种潮玩商品&#xff0c;还拥有各种游戏玩法&#xff0c;尤其是大逃杀游戏非常火爆&#xff01;本文将介绍大逃杀游戏的开发和发展前景。 大逃杀游戏 大逃杀游戏是当下的一种新型游戏模式&#xff0c;旨在为玩家提供…

odoo17 | 计算字段和更改事件

前言 模型之间的关系是任何Odoo模块的关键组成部分。它们是任何业务案例建模所必需的。然而&#xff0c;我们可能希望给定模型中的字段之间存在链接。有时一个字段的值是由其他字段的值决定的&#xff0c;而有时我们希望帮助用户进行数据输入。 这些案例得到了计算字段和onch…

JavaScript 基础四 函数使用、传参、返回值、作用域、匿名函数

JavaScript 基础四 函数 1.1 函数1.2 函数使用函数名命名规范函数的调用 1.3 函数传参&#xff08;1&#xff09;声明语法&#xff08;2&#xff09;调用有参函数&#xff08;3&#xff09;形参和实参&#xff08;4&#xff09;参数默认值 1.4 函数返回值&#xff08;1&#xf…

HTML如何设置多图片上传,并限制格式类型

在HTML如何设置多图片上传&#xff0c;并限制格式类型为jpg和png格式。 <input type"file" name"fileInput" id"fileInput"> 上面这行代码&#xff0c;只支持单个文件上传&#xff0c;且不支持文件类型过滤&#xff0c;在实际开发过程中&…

Mybatis Plus 条件构造器的简单介绍 以及IService 的简单使用

文章目录 Mybatis Plus条件构造器LambdaWrapper自定义SQLService接口 Mybatis Plus 条件构造器 简单案例演示 特殊案例更新 UpdateWrapper 更新几个用户的余额减少200LambdaWrapper 因为上方为硬编码&#xff0c;所以不建议 而是使用反射&#xff08;Lambda&#xff09;这样…

AI的突破与融合:2024年中国智能技术的新纪元_光点科技

随着人工智能领域的不断突破&#xff0c;2024年注定将成为中国智能技术发展的一个新纪元。当下&#xff0c;AI技术不仅在理论研究上取得了重大进展&#xff0c;其在商业应用、社会服务等领域的融合也日益深入。本文将结合近期网络上的AI热点&#xff0c;展望中国在AI技术方面的…