Hammy学习操作系统-内存

411 阅读28分钟

1.什么是内存,内存是用来干什么的

1.1 冯诺依曼体系结构

手机把cpu、内存、网络通信、摄像头芯片都集成在一个芯片。这种方式是Soc,也就是System on a Chip(系统芯片)。 这样看起来,个人电脑和手机的硬件组成方式不太一样。可是,我们写只能手机上的App,和写个人电脑的客户端应用似乎没有什么差别,都是通过高级语言撰写、编译之后,一样是把代码和数据加载到内存里执行。这是为什么? 因为无论是pc、server、smartphone都遵循着一个"计算机"的抽象概念。冯诺依曼提出的冯诺依曼体系结构,也叫存储程序计算机。

什么是存储程序计算机?暗含两个概念,一个是"可编程"计算机,一个是"存储计算机"。 说道"可编程",什么是"不可编程"。计算机是由各种门电路组合而成的,然后通过组装出一个固定的电路板,来完成一个特定的计算程序。一旦需要修改功能,就要重新组装电路。 这样的话,计算机就是"不可编程的",因为程序在计算机硬件层面是“写死的”。最常见的就是老式计算器,电路板设好了加减乘除,做不了任何计算逻辑固定之外的事情。

我们再来看“存储”计算机。这其实是说,程序本身是存储在计算机的内存中的,可以通过加载不同的程序来解决不同的问题。有”存储“就一定有不能存储程序的计算机。最早的plugboard(Engima Machine)就是可以通过板子上不同的插头或者接口的位置插入线路,来实现不同的功能。这样的计算机自然是可编程的,但是编写好的程序不能存储下来下一次加载使用。

冯诺依曼First Draft描述他心目中的计算机:

  • 首先是一个包含算数逻辑单元和处理器寄存器的处理器单元,用来完成各种算术和逻辑运算。因为它能够完成各种数据的处理或者计算工作,有人叫做数据通路或者运算器。
  • 接着用来存储数据(Data)和指令的内存。以及更大容量的外部存储,现在通常是硬盘。
  • 最后是各种输入和输出设备,网卡、鼠标、显示器 而所有的计算机程序,也都可以抽象为从Input Device读取输入信息,通过运算器和控制器来执行存储到存储器里的程序,最终把结果输出到输出设备中。无论是高级还是低级语言的程序,都是基于这个抽象框架来运作的。

1.2 理解存储器的层次结构

随机存取存储器(英语:Random Access Memory,缩写:RAM;也叫主存)是与CPU直接交换数据的内部存储器。 它可以随时读写,而且速度很快,通常作为操作系统或其他正在运行中的程序的临时数据存储介质。

在有计算机之前,我们通常把信息和数据存储在书、文件这样的物理介质里面。有了计算机之后,我们通常把数据存储在计算机的存储器里面。 而存储器系统是一个通过各种不同的方法和设备,一层一层组合起来的系统。 下面,我们把计算机的存储器层次结构和我们日常生活里处理信息、阅读书籍做个对照,好让你更容易理解、记忆存储器的层次结构。

我们常常把 CPU 比喻成计算机的“大脑”。我们思考的东西,就好比 CPU 中的寄存器(Register)。 寄存器与其说是存储器,其实它更像是 CPU 本身的一部分,只能存放极其有限的信息,但是速度非常快,和 CPU 同步。

而我们大脑中的记忆,就好比 CPU Cache(CPU 高速缓存,我们常常简称为“缓存”)。CPU Cache 用的是一种叫作 SRAM(Static Random-Access Memory,静态随机存取存储器)的芯片。

1.2.1 SRAM

SRAM 之所以被称为“静态”存储器,是因为只要处在通电状态,里面的数据就可以保持存在。而一旦断电,里面的数据就会丢失了。 在 SRAM 里面,一个比特的数据,需要 6~8 个晶体管。所以 SRAM 的存储密度不高。 同样的物理空间下,能够存储的数据有限。不过,因为 SRAM 的电路简单,所以访问速度非常快。

1.2.2 DRAM

内存用的芯片和 Cache 有所不同,它用的是一种叫作 DRAM(Dynamic Random Access Memory,动态随机存取存储器)的芯片, 比起 SRAM 来说,它的密度更高,有更大的容量,而且它也比 SRAM 芯片便宜不少。DRAM 被称为“动态”存储器,是因为 DRAM 需要靠不断地“刷新”,才能保持数据被存储起来。 DRAM 的一个比特,只需要一个晶体管和一个电容就能存储。所以,DRAM 在同样的物理空间下,能够存储的数据也就更多,也就是存储的“密度”更大。 但是,因为数据是存储在电容里的,电容会不断漏电,所以需要定时刷新充电,才能保持数据不丢失。DRAM 的数据访问电路和刷新电路都比 SRAM 更复杂,所以访问延时也就更长。

1.2.3 存储器的层级结构

整个存储器的层次结构,其实都类似于 SRAM 和 DRAM 在性能和价格上的差异。 SRAM 更贵,速度更快。DRAM 更便宜,容量更大。SRAM 好像我们的大脑中的记忆,而 DRAM 就好像属于我们自己的书桌。

大脑(CPU)中的记忆(L1 Cache),不仅受成本层面的限制,更受物理层面的限制。这就好比 L1 Cache 不仅昂贵,其访问速度和它到 CPU 的物理距离有关。 芯片造得越大,总有部分离 CPU 的距离会变远。电信号的传输速度又受物理原理的限制,没法超过光速。所以想要快,并不是靠多花钱就能解决的。

我们自己的书房和书桌(也就是内存)空间一般是有限的,没有办法放下所有书(也就是数据)。 如果想要扩大空间的话,就相当于要多买几平方米的房子,成本就会很高。 于是,想要放下更多的书,我们就要寻找更加廉价的解决方案。没错,我们想到了公共图书馆。对于内存来说,SSD(Solid-state drive 或 Solid-state disk,固态硬盘)、HDD(Hard Disk Drive,硬盘)这些被称为硬盘的外部存储设备,就是公共图书馆。 于是,我们就可以去家附近的图书馆借书了。图书馆有更多的空间(存储空间)和更多的书(数据)。

从 Cache、内存,到 SSD 和 HDD 硬盘,一台现代计算机中,就用上了所有这些存储器设备。 其中,容量越小的设备速度越快,而且,CPU 并不是直接和每一种存储器设备打交道,而是每一种存储器设备,只和它相邻的存储设备打交道。 比如,CPU Cache 是从内存里加载而来的,或者需要写回内存, 并不会直接写回数据到硬盘,也不会直接从硬盘加载数据到 CPU Cache 中,而是先加载到内存,再从内存加载到 Cache 中。

这样,各个存储器只和相邻的一层存储器打交道,并且随着一层层向下, 存储器的容量逐层增大,访问速度逐层变慢,而单位存储成本也逐层下降,也就构成了我们日常所说的存储器层次结构。

1.3 问题

SDRAM这么快,为什么还要有其他存储介质?

我们常说的内存指的是哪一部分?对于我们开发者,我们使用的那部分内存?

在上世纪 80~90 年代,3.5 寸的磁盘大行其道。它的存储空间只有 1.44MB,比起当时 40MB 的硬盘,它却被大家认为是“海量”存储的主要选择。你猜一猜这是为什么?

1.4 问题答案(个人想法,仅供参考)

SDRAM这么快,为什么还要有其他存储介质?

  • 1.价格因素
  • 2.SDRAM是易失性的,不能持久化

我们常说的内存指的是哪一部分?对于我们开发者,我们使用的那部分内存?

  • DRAM那部分内存,因为存储器只和相邻的一层打交道,所以高级语言是不能直接在寄存器中开辟使用内存的。只能通过在内存中开辟,然后CPU在通过读取内存,到缓存和寄存器中。

在上世纪 80~90 年代,3.5 寸的磁盘大行其道。它的存储空间只有 1.44MB,比起当时 40MB 的硬盘,它却被大家认为是“海量”存储的主要选择。你猜一猜这是为什么?

  • 因为好携带,就如同我们现在存储都用u盘而不是移动硬盘。

2.内存是怎么工作的

我们现在所讲的内存一般指的是虚拟内存。如果想了解什么虚拟内存,我们需要先确定物理内存的概念

物理内存

物理内存: 真实的内存,就是我们常说的4G、8G内存条

2.1 内存使用的演化

2.1.1 没有内存抽象的年代

最简单的存储器抽象就是没有抽象。早期,每一个程序都直接访问物理内存。当执行一个程序执行如下指令: MOV REGISTER1,1000

无内存抽象存在的问题

这条指令会毫无想象力的将物理地址1000中的内容赋值给寄存器。不难想象,这种内存操作方式使得操作系统中存在多进程变得完全不可能。当一个进程给内存地址1000赋值后,另一个进程也同样给内存地址赋值,那么第二个进程对内存的赋值会覆盖第一个进程所赋的值,这回造成两条进程同时崩溃。

  • 用户程序可以访问任意内存,容易破坏操作系统,造成崩溃

  • 同时运行多个程序特别困难

2.2 内存抽象:地址空间

如果每个进程都有一套属于自己的地址集合,这个地址空间独立于其他进程的地址空间,这样是不是就解决了直接访问物理内存存在的问题。

地址空间的出现就解决了直接访问物理内存产生的两个问题。但是在我们真正的使用场景中,大多数情况是不知道要用多少内存的,而且我这个内存可能还会动态增加,甚至进程使用的内存完全超过了物理内存的容量。

物理内存地址集合存在的问题:

  • 需要先确定进程使用的最大内存,这往往是不可预测的
  • 如果使用的内存超过了物理内存的最大容量,就GG了

2.3 虚拟内存

2.3.1 基本概念

虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。

对虚拟内存的定义是基于对地址空间的重定义,即把地址空间定义为“连续的虚拟内存地址”,以借此“欺骗”程序,使它们以为自己正在使用一大块的“连续”地址。

如果是 32 位,有 2^32 = 4G 的内存空间都是我的,不管内存是不是真的有 4G。如果是 64 位,在 x86_64 下面,其实只使用了 48 位,那也挺恐怖的。48 位地址长度也就是对应了 256TB 的地址空间。我都没怎么见过 256T 的硬盘,别说是内存了。

虚拟内存的出现解决了地址空间集合存在的问题

这时候,有的同学就会问。物理内存的容量是有限的,你用虚拟内存来管理,也没办法跨越物理内存容量使用呀

如果我们一次性申请的内存空间超过了物理内存的话,我们还有磁盘空间,这就衍生了一个新的概念,内存交换。

2.4 内存交换

  • 使用场景: 物理内存的使用超过了支持的最大范围
  • 原理:把处于等待(阻塞)状态(或在CPU调度原则下被剥夺运行权利)的程序(进程)从内存移到磁盘,把内存空间腾出来,这一过程又叫换出。把准备好竞争CPU运行的程序从移到内存,这一过程又称为换入

所以我们理论使用的内存大小的极限为物理内存+磁盘空间,但是我们都知道磁盘在存储器结构中属于最廉价、速度也是最慢的一层。如果真的大量的开始swap,那么进程的指令执行效率就会很低。

2.5 内存分页和分段

2.5.1 我们使用的内存,为什么需要分页或者分段

区域划分的目的可以解决安全问题,我们都知道现代操作系统都分用户态和内核态。通过以分段的方式对内存空间进行划分,来区分权限。例如在32位操作系统中,3G内存空间是用户态内存空间,1G是内核态内存空间。

2.5.2 内存分段

这种找出一段连续的物理内存和虚拟内存地址进行映射的方法,我们叫分段(Segmentation)。这里的段,就是指系统分配出来的那个连续的内存空间。

分段的办法很好,解决了程序本身不需要关心具体的物理内存地址的问题,通过分段限制进程的访问权限等。但它也有不足之处,第一个就是内存碎片问题

例子

我们来看这样一个例子。我现在手头的这台电脑,有 1GB 的内存。我们先启动一个图形渲染程序,占用了 512MB 的内存,接着启动一个 Chrome 浏览器,占用了 128MB 内存,再启动一个 Python 程序,占用了 256MB 内存。这个时候,我们关掉 Chrome,于是空闲内存还有 1024 - 512 - 256 = 256MB。按理来说,我们有足够的空间再去装载一个 200MB 的程序。但是,这 256MB 的内存空间不是连续的,而是被分成了两段 128MB 的内存。因此,实际情况是,我们的程序没办法加载进来

当然,我们可以用上面讲到的内存交换解决这个问题。但是内存交换这种依赖磁盘当做中介的方案会严重影响性能。那我们怎么解决分段引起的内存碎片的问题?

2.5.3 内存分页

既然问题出在内存碎片和内存交换的空间太大上,那么解决问题的办法就是,少出现一些内存碎片。另外,当需要进行内存交换的时候,让需要交换写入或者从磁盘装载的数据更少一点,这样就可以解决这个问题。这个办法,在现在计算机的内存管理里面,就叫作内存分页。

和分段这样分配一整段连续的空间给到程序相比,分页是把整个物理内存空间切成一段段固定尺寸的大小。而对应的程序所需要占用的虚拟内存空间,也会同样切成一段段固定尺寸的大小。这样一个连续并且尺寸固定的内存空间,我们叫页(Page)。从虚拟内存到物理内存的映射,不再是拿整段连续的内存的物理地址,而是按照一个一个页来的。页的尺寸一般远远小于整个程序的大小。在 Linux 下,我们通常只设置成 4KB。你可以通过命令看看你手头的 Linux 系统设置的页的大小。

$ getconf PAGE_SIZE

分页的方式使得我们在加载程序的时候,不再需要一次性都把程序加载到物理内存中。我们完全可以在进行虚拟内存和物理内存的页之间的映射之后,并不真的把页加载到物理内存里,而是只在程序运行中,需要用到对应虚拟内存页里面的指令和数据时,再加载到物理内存里面去。

即使内存空间不够,需要让现有的、正在运行的其他程序,通过内存交换释放出一些内存的页出来,一次性写入磁盘的也只有少数的一个页或者几个页,不会花太多时间,让整个机器被内存交换的过程给卡住。分页实际上是降低了分段的粒度

缺页错误

通过分页的描述,我们知道分页的方式使我们加载程序的时候,不会真的把页加载到内存里,而是我们在用到的时候才会进行加载。而当我们读取特定的页,却发现数据并没有加载到物理内存的时候,就会触发一个CPU的缺页错误,也就是我们常说的缺页中断。

我们如何管理虚拟地址到物理地址的映射?

页表

想要把虚拟内存地址,映射到物理内存地址,最直观的办法,就是来建一张映射表。这个映射表,能够实现虚拟内存里面的页,到物理内存里面的页的一一映射。这个映射表,在计算机里面,就叫作页表(Page Table)

页表这个地址转换的办法,会把一个内存地址分成页号(Directory)和偏移量(Offset)两个部分。这么说太理论了,我以一个 32 位的内存地址为例,帮你理解这个概念。

其实,前面的高位,就是内存地址的页号。后面的低位,就是内存地址里面的偏移量。做地址转换的页表,只需要保留虚拟内存地址的页号和物理内存地址的页号之间的映射关系就可以了。同一个页里面的内存,在物理层面是连续的。以一个页的大小是 4K 字节(4KB)为例,我们需要 20 位的高位,12 位的低位。

总结一下,对于一个内存地址转换,其实就是这样三个步骤:

  • 把虚拟内存地址,切分成页号和偏移量的组合;
  • 从页表里面,查询出虚拟页号,对应的物理页号;
  • 直接拿物理页号,加上前面的偏移量,就得到了物理内存地址。

看起来这个逻辑似乎很简单,很容易理解,不过问题马上就来了。你能算一算,这样一个页表需要多大的空间吗?

不知道你算出的数字是多少?32 位的内存地址空间,页表一共需要记录 2^20 个到物理页号的映射关系。这个存储关系,就好比一个 2^20 大小的数组。一个页号是完整的 32 位的 4 字节(Byte),这样一个页表就需要 4MB 的空间。听起来 4MB 的空间好像还不大啊,毕竟我们现在的内存至少也有 4GB,服务器上有个几十 GB 的内存和很正常。

虽然4MB看起来不大,但是页表是按照进程为维度的。每个进程都有自己独立的虚拟内存地址空间。操作系统一般运行至少运行几百个进程,在32位操作中几百个进程就会占用G级别的物理内存,那64位的计算机和操作系统占用的内幕才能就会更恐怖。

多级页表

仔细想一想,我们其实没有必要存下这 2^20 个物理页表啊。大部分进程所占用的内存是有限的,需要的页也自然是很有限的。简单来说,就是我们不再存所有的页面映射关系,而我们只存我们用到的页面映射关系。简单来说就说,从一开始全部分配到按需分配。如果是按需分配的话,我们怎么定位到我们要使用到的物理页面呢?

单级页表在内存中必须是连续的

单级页表为了随机访问必须连续存储,如果虚拟内存空间很大,就需要很多页表项,就需要很大的连续内存空间。为什么必须是连续存储的?因为单级页表是直接与物理内存进行映射的,寻址的方式通过页号+偏移量,这就如同数组这种线性表一样,首先得是连续才能通过数组下标找到地址。

页表中所有页表项必须提前建好,并且要求是连续的。如果不连续,就没有办法通过虚拟地址里面的页号找到对应的页表项了。那怎么办呢?我们可以试着将页表再分页,4G 的空间需要 4M 的页表来存储映射。我们把这 4M 分成 1K(1024)个 4K,每个 4K 又能放在一页里面,这样 1K 个 4K 就是 1K 个页,这 1K 个页也需要一个表进行管理,我们称为页目录表,这个页目录表里面有 1K 项,每项 4 个字节,页目录表大小也是 4K。

多级页表本质上就是将连续存储变成了离散存储,这样我们就不需要一开始像单级页表那样需要将地址全部分配了

不过,多级页表虽然节约了我们的存储空间,却带来了时间上的开销,所以它其实是一个“以时间换空间”的策略。原本我们进行一次地址转换,只需要访问一次内存就能找到物理页号,算出物理内存地址。但是,用了 4 级页表,我们就需要访问 4 次内存,才能找到物理页号了。

当然对于 64 位的系统,两级肯定不够了,就变成了四级目录,分别是全局页目录项 PGD(Page Global Directory)、上层页目录项 PUD(Page Upper Directory)、中间页目录项 PMD(Page Middle Directory)和页表项 PTE(Page Table Entry)。

我们假设只给这个进程分配了一个数据页。如果只使用页表,也需要完整的 1M 个页表项共 4M 的内存,但是如果使用了页目录,页目录需要 1K 个全部分配,占用内存 4K,但是里面只有一项使用了。到了页表项,只需要分配能够管理那个数据页的页表项页就可以了,也就是说,最多 4K,这样内存就节省多了。

TLB

从虚拟内存地址到物理内存地址的转换,我们通过页表这个数据结构来处理。为了节约页表的内存存储空间,我们会使用多级页表数据结构。

多级页表虽然节约了我们的存储空间,但是却带来了时间上的开销,变成了一个“以时间换空间”的策略。原本我们进行一次地址转换,只需要访问一次内存就能找到物理页号,算出物理内存地址。但是用了 4 级页表,我们就需要访问 4 次内存,才能找到物理页号。

我们知道,内存访问其实比 Cache 要慢很多。我们本来只是要做一个简单的地址转换,现在反而要一下子多访问好多次内存。 程序所需要使用的指令,都顺序存放在虚拟内存里面。我们执行的指令,也是一条条顺序执行下去的。也就是说,我们对于指令地址的访问。

因此,这连续 5 次的内存地址转换,其实都来自于同一个虚拟页号,转换的结果自然也就是同一个物理页号。那我们就可以用前面几讲说过的,用一个“加个缓存”的办法。把之前的内存转换地址缓存下来,使得我们不需要反复去访问内存来进行内存地址转换。

计算机工程师们专门在 CPU 里放了一块缓存芯片。这块缓存芯片我们称之为 TLB,全称是地址变换高速缓冲(Translation-Lookaside Buffer)。这块缓存存放了之前已经进行过地址转换的查询结果。这样,当同样的虚拟地址需要进行地址转换的时候,我们可以直接在 TLB 里面查询结果,而不需要多次访问内存来完成一次转换。

为了性能,我们整个内存转换过程也要由硬件来执行。在 CPU 芯片里面,我们封装了内存管理单元(MMU,Memory Management Unit)芯片,用来完成地址转换。和 TLB 的访问和交互,都是由这个 MMU 控制的。

2.6 置换算法

当发生缺页中断时,如果物理内存已经没有可用的空间。 os必须在内存中选择一个页面将其换出内存,以便为即将调入的页面腾出空间。如果要换出的页面在内存驻留期间被修改过,就必须把它写回磁盘以更新改页面在磁盘上的副本;

当发生缺页中断时,虽然可以随机选择一个页面来置换,但是如果每次都选择不常使用的页面会提升系统个性能。如果被一个频繁使用的页面被置换出内存,很可能短时间内又要被调入内存,会带来不必要的开销。

最优页面置换算法

很容易可以描述出最好的页面置换算法,虽然该算法不可能实现。在缺页中断发生时,有些页面在内存中,其中有一个页面将很快访问,其他页面则可能要10、100或1000指令后才会被访问,每个页面都可以用在该页面首次被访问前要执行的指令数作为标记。

最优页面置换算法规定应该置换标记最大的页面。如果一个页面在800万条指令内不会被使用,另外一个页面在600万指令内不会被使用,则置换前一个页面。

这个算法唯一的问题是它是无法实现的。当缺页中断发生时,os是无法知道下一次将在什么被访问

最近未使用页面置换算法(LRU)

为使操作系统能够收集有用的统计信息,使大部分具有虚拟内存的计算机中,系统为每一个页面设置了两个状态位。当页面被访问时设置R位;当页面被写入设置M位。

当发生缺页中断时,操作系统检查所有的页面并根据它们之前的R位和M位的值,分为4类:

  • 没有被访问,没有被修改
  • 没有被访问,已被修改
  • 已被访问,没有被修改
  • 已被访问,已被修改

NRU(not recently used)算法随机从类编号最小的非空类中挑选一个页面淘汰。这个算法隐含的意思是,在最近一个时钟滴答中淘汰一个没有被访问的已修改页面要比淘汰一个被频繁使用的页面好。

先进先出页面置换算法(FIFO)

FIFO置换算法。OS维护一个所有当前内存中的页面链表,最新进入的页面放在表尾。最早进入的页面放在表头。当发生缺页中断时,淘汰表头的页面并把新调入的页面加到表尾。

最近最少使用页面置换算法

对最优算法的一个很好的近似是基于这样的观察:最前面几条指令中频繁使用的页面很可能在后面的几条指令被使用。反过来说,已经很久没有使用的页面很有可能在未来较长一段时间内仍然不会被使用。这个思想是一个可实现的算法。

虽然可实现,但是代价很高。需要维护一个所有页面的链表。

2.7 问题

在实际的虚拟内存地址到物理内存地址的地址转换的过程里,我们没有采用哈希表,而是采用了多级页表的解决方案。你能想一想,使用多级页表,对于哈希表有哪些优点,又有哪些缺点吗?

在 Java 这样使用虚拟机的编程语言里面,我们写的程序是怎么装载到内存里面来的呢?它也和我们讲的一样,是通过内存分页和内存交换的方式加载到内存里面来的么?

为什么页表设置为4kb大小,设置为大一点不好么?

2.8 问题答案(个人理解)

在实际的虚拟内存地址到物理内存地址的地址转换的过程里,我们没有采用哈希表,而是采用了多级页表的解决方案。你能想一想,使用多级页表,对于哈希表有哪些优点,又有哪些缺点吗?

  • 哈希表有哈希冲突 并且顺序乱 不符合局部性原理 所以页表存储更复合计算机运行特点 64位系统的快表应该是对页表快速查询的一个优化吧

在 Java 这样使用虚拟机的编程语言里面,我们写的程序是怎么装载到内存里面来的呢?它也和我们讲的一样,是通过内存分页和内存交换的方式加载到内存里面来的么?

  • jvm已经是上层应用,无需考虑物理分页,一般更直接是考虑对象本身的空间大小,物理硬件管理统一由承载jvm的操纵系统去解决吧

为什么页表设置为4kb大小,设置为大一点不好么?

  • 选择小页面有一个明显的好处,考虑一个程序,它分成8个阶段执行,每阶段需要4Kb内存。 如果页面大小是32KB,那么必须始终给程序分配32KB内存。如果页面大小是4B,那么任何时刻都只需要4KB内存。总体来说,大尺寸页面比小尺寸页面浪费了更多内存。
  • 小页面也能充分的利用TLB空间,因为TLB本身是硬件,容量本身优先,而且如果空间太大,会提高时间复杂度。

3.Linux的内存管理是什么样子的(待补充)

4.我们学会了内存可以应用在什么场景呢?

4.1 线程池设置

我们知道当页表对应没有映射物理内存的时候,CPU会发生缺页中断。缺页中断后,操作系统会将物理内存映射到对应页表,如果内存空间吃紧,那么就会swap也就是内存交换。这个操作过程并不是一个计算密集型的,而更多是一个IO操作,所以我们可以用一个线程去处理缺页中断

对于 CPU 密集型计算,多线程本质上是提升多核 CPU 的利用率,所以对于一个 4 核的 CPU,每个核一个线程,理论上创建 4 个线程就可以了,再多创建线程也只是增加线程切换的成本。所以,对于 CPU 密集型的计算场景,理论上“线程的数量 =CPU 核数”就是最合适的。不过在工程上,线程的数量一般会设置为“CPU 核数 +1”,这样的话,当线程因为偶尔的内存页失效或其他原因导致阻塞时,这个额外的线程可以顶上,从而保证 CPU 的利用率。

4.2 局部性原理(待补充)

4.3 高速缓存(待补充)

5.引用

blog.csdn.net/hguisu/arti…

zh.wikipedia.org/wiki/%E8%99…

time.geekbang.org/column/arti…

www.zhihu.com/question/63…