Linux内存的分页管理详解||物理内存||虚拟内存||联系||分页机制||工作原理

目录

 

1.内存

2.虚拟内存

3.内存的分页管理

1.虚拟内存地址到物理内存地址的寻址

2.磁盘地址(虚拟内存空间)到物理内存地址的映射

3.虚拟内存是如何工作的


1.内存

内存是计算机的主存储器,它为进程开辟出进程空间,让进程在其中保存数据。它的最小的存储单位一般是字节。内存会为每各字节进行编号,从0开始依次递增(线性的)。通常我们会用16进制来表示内存的地址。同时,内存地址的编号范围和地址总线的个数是直接相关的,例如地址总线的个数为32,那么内存的地址编号范围为0x00000000到0xFFFFFFFF(为2的32次方)。

当代操作系统既要求能够使多个程序共享系统资源,同时还要求内存限制对程序开发者透明,在此需求下,虚拟内存应运而生。

2.虚拟内存

虚拟内存实际上是在磁盘上划分一块进程专属的空间去充当内存。虚拟内存(逻辑地址)支持程序访问比系统物理可用内存大的多的内存空间,而且也使得多个程序共享内存变量变得高效。虚拟内存依靠透明的使用磁盘空间,得以使程序运行起来好像它们使用比系统物理内存更多的内存空间。磁盘相比于物理内存更加低廉,而且容量巨大,因此可以作为物理内存的扩充。之所以称虚拟内存,就是因为磁盘存储体可以有效的充当内存,但它并不是内存。

下图描述了多级数据存储体的层次关系:

运行中的每个进程都有自己的一套虚拟内存空间,空间的大小一般与地址总线的个数有关,32为操作系统的虚拟内存大小为2的32次方(4G)。同时,虚拟内存的编号方法与物理内存类似。从功能上说,虚拟内存地址和物理内存地址的功能是相同的,都是为数据提供位置索引。由于每个进程都有一份独立的虚拟内存空间,所以不同进程中虚拟地址相同的地址表示数据很可能不一样,如下图所示:(图中的进程空间相当于磁盘上的某一块空间)

因此,进程对物理内存是一无所知的,因为虚拟内存的存在将进程与物理内存隔离开来,只能通过MMU(内存管理单元)将虚拟内存地址转换成物理内存地址,间接的去访问物理内存。从本质上说,虚拟内存的存在,剥夺了进程直接访问物理内存的权力,但是吗,正是因为如此,才保证进程之间的相互独立性,减少了进程出错的可能性。

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

使用虚拟内存的优点:

可以弥补物理内存大小的缺陷,使每个进程都有自己的独立空间,同时使数据共享成为可能。

缺点:

使用虚拟内存会占用一定的磁盘空间,加大了对磁盘的读写。

3.内存的分页管理

使用虚拟内存的时候,我们需要建立虚拟地址与物理地址的对应关系,如果我们继续使用存储的最小单位(字节)充当存储的基本单元,那么就需要为每个字节都去建立一个对应关系,这样会消耗巨大的计算机存储资源。因此操作系统采用分页的机制来建立这种对应关系,所谓分页,就是以更大的尺寸去充当存储的基本单元,在Linux系统中一个页的大小通常是4KB=4096字节。

linux分页管理即是:

将虚拟地址空间以4KB的大小,分成若干个,对页进行编号(从0开始),并从0开始在页内进行编号。首先看如下32位寻址空间的虚拟地址结构图:

上述数字均为16进制,前12位表示页内偏移地址,后20位表示页号,因此在进程的虚拟地址空间中,页的大小和页的数目分别如下:

2^12 = 4KB

2^20 = 1M

因此32位系统的寻址能力位:1M x4KB = 4G.

同时,物理内存也会按照同样的大小(4KB),分成框或块,也是对块进行编号,同时块内进行编号。

同时操作系统还会通过维护一张表(页表),记录了每一页和框的对应关系。如下图:(图中是简单的单级页表,linux中采用了多级页表,道理一样,都是逐层建立对应关系)

页-页表-框

由于各个进程的各个页离散的存储在内粗内得不同物理块上,页表的存在,使得进程能在内存中找到每个页面所对应的物理块,保证了进程的正确运行。

内存的分页,可以极大地减少所要记录的内存的对应关系。由于每页的大小式4096个字节,因此内存中的总页数只是总字节数的4096分之一。对应关系也缩减为以字节为基本存储单元的4096分之一。分页让虚拟内存地址的设计有了实现的可能。

1.虚拟内存地址到物理内存地址的寻址

对于某特定机器,其地址结构式一定的。若给定一个逻辑地址空间中的地址A,页面大小L,则页号P和页内地址可通过下面方式求得:

P = int[A/L]   []表示向下取整。

d = A %L

根据页表和页号P可以找到物的理内存中对应块,数据的地址就位于块内的地址d处。

2.磁盘地址(虚拟内存空间)到物理内存地址的映射

由于程序的数据一般先存储与磁盘中(不是内存),当我们使用虚拟内存的时候,磁盘中的数据也被分割成以页为单位的基本单元,页可以在磁盘与内存之间来回移动。这样,程序正在使用的那部分数据所属的页就可以从磁盘中至于内存中,以便进行快速的访问,而未使用的部分则被临时存储在磁盘中,如此可以减轻待访问数据存放在磁盘中而导致读取时间过长的问题。当进程访问一个地址时,该地址所在的页被载入内存,对页中任意一数据的请求都会变成对该页的访问。如果页中的任一地址以前都没有访问过,说明该页并没有装入内存(因为只有进程需要,才会将对应页装入内存,所以装入内存中的页一定是访问过的)。对页中地址的第一次访问便会产生一个失败或缺页(因为第一次访问的时候,所访问的页肯定不在内存中),因此必须从磁盘中进行请求。当发生缺页的时候,内核必须选择一个页面,然后将其内容写回到磁盘,从而用程序刚刚请求的页的内容来填充它。页面的选择通过缺页置换算法来确定。

3.虚拟内存是如何工作的

当每个进程创建的时候,内核会为进程分配4G的虚拟内存,当进程还没有开始运行时,这只是一个内存布局。实际上并不立即就把虚拟内存对应位置的程序数据和代码(比如.text .data段)拷贝到物理内存中,只是建立好虚拟内存和磁盘文件之间的映射就好(叫做存储器映射)。这个时候数据和代码还是在磁盘上的。当运行到对应的程序时,进程去寻找页表,发现页表中地址没有存放在物理内存上,而是在磁盘上,于是发生缺页异常,于是将磁盘上的数据拷贝到物理内存中。

另外在进程运行过程中,要通过malloc来动态分配内存时,也只是分配了虚拟内存,即为这块虚拟内存对应的页表项做相应设置,当进程真正访问到此数据时,才引发缺页异常。

可以认为虚拟空间都被映射到了磁盘空间中(事实上也是按需要映射到磁盘空间上,通过mmap,mmap是用来建立虚拟空间和磁盘空间的映射关系的)
 

参考文章:

https://www.cnblogs.com/vamei/p/9329278.html

https://www.cnblogs.com/leohahah/p/6921731.html

https://baike.baidu.com/item/基本分页存储管理方式/7866872?fr=aladdin

https://blog.csdn.net/u014338577/article/details/82750771

《unix内核编程》

发布了143 篇原创文章 · 获赞 35 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_42214953/article/details/105558471