Linux的内存分页管理

  • 时间:
  • 浏览:2

作者:Vamei 出处:http://www.cnblogs.com/vamei 严禁转载

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

内存

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

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

内存的存储单元采用了随机读取存储器(RAM, Random Access Memory)。所谓的“随机读取”,是指存储器的读取时间和数据所在位置无关。与之相对,也不有存储器的读取时间和数据所在位置有关。就拿磁带来说,当我门 想听其中的一首歌,前要转动带子。原困那首歌是第一首,没办法 立即就能只能播放。原困那首歌恰巧是最后一首,当我门 快进到能只能播放的位置就前要花很长时间。当我门 原困知道,多线程 池池前要调用内存中不同位置的数据。原困数据读取时间和位置相关励志的话 ,计算机就不难 把控多线程 池池的运行时间。也不,随机读取的行态是内存成为主存储器的关键因素。

内存提供的存储空间,除了能满足内核的运行需求,还通常能支持运行中的多线程 池池。即使多线程 池池所需空间超过内存空间,内存空间也能只能通过几滴 拓展来弥补。换句话说,内存的存储能力,和计算机运行情况报告的数据总量相当。内存的缺点是只能持久地保存数据。一旦断电,内存中的数据就会消失。也不,计算机即使有了内存原来 六个 主存储器,还是前要硬盘原来 的实物存储器来提供持久的储存空间。

虚拟内存

内存的一项主要任务,也不存储多线程 池池的相关数据。当我门 以前 原困看了太多线程 池池空间的多线程 池池段、全局数据、栈和堆,以及你你你这个 你你你这个 存储行态在多线程 池池运行中所起到的关键作用。有趣的是,尽管多线程 池池和内存的关系没办法 紧密,但多线程 池池并只能直接访问内存。在Linux下,多线程 池池只能直接读写内存中地址为0x1位置的数据。多线程 池池中能访问的地址,只能是虚拟内存地址(virtual memory address)。操作系统会把虚拟内存地址翻译成真实的内存地址。你你你这个 内存管理最好的辦法 ,称为虚拟内存(virtual memory)。

每个多线程 池池有的是当事人的一套虚拟内存地址,用来给当事人的多线程 池池空间编号。多线程 池池空间的数据同样以字节为单位,依次增加。从功能上说,虚拟内存地址和物理内存地址类似于 ,有的是为数据提供位置索引。多线程 池池的虚拟内存地址相互独立。也不,六个 多线程 池池空间能只能有相同的虚拟内存地址,如0x60 060 0。虚拟内存地址和物理内存地址又有一定的对应关系,如图1所示。对多线程 池池某个虚拟内存地址的操作,会被CPU翻译成对某个具体内存地址的操作。

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

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

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

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

当事人面,有了虚拟内存地址,内存共享也变得简单。操作系统能只能把同一物理内存区域对应到多个多线程 池池空间。原来 ,不前要任何的数据克隆qq,多个多线程 池池就能只能看了相同的数据。内核和共享库的映射,也不通过你你你这个 最好的辦法 进行的。每个多线程 池池空间中,最初一部分的虚拟内存地址,都对应到物理内存中预留给内核的空间。原来 ,所有的多线程 池池就能只能共享同一套内核数据。共享库的情况报告也是类似于 。对于任何六个 共享库,计算机只前要往物理内存中加载一次,就能只能通过操纵对应关系,来让多个多线程 池池并肩使用。IPO中的共享内存,有的是赖于虚拟内存地址。

内存分页

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

记录对应关系最简单的最好的辦法 ,也不把对应关系记录在一张表中。为了让翻译带宽足够地快,你你你这个 表前要加载在内存中。不过,你你你这个 记录最好的辦法 惊人地浪费。原困树莓派1GB物理内存的每个字节有的是六个 对应记录励志的话 ,没办法 光是对应关系就要远远超过内存的空间。原困对应关系的条目众多,搜索到六个 对应关系所需的时间也很长。原来 励志的话 ,会让树莓派陷入瘫痪。

也不,Linux采用了分页(paging)的最好的辦法 来记录对应关系。所谓的分页,也不以更大尺寸的单位页(page)来管理内存。在Linux中,通常每页大小为4KB。原困想要获取当前树莓派的内存页大小,能只能使用命令:

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

4096

返回的4096代表每个内存页能只能存放4096个字节,即4KB。Linux把物理内存和多线程 池池空间都分割成页。

内存分页,能只能极大地减少所要记录的内存对应关系。当我门 原困看了,以字节为单位的对应记录实在没办法 来太多。原困把物理内存和多线程 池池空间的地址都分成页,内核只前要记录页的对应关系,相关的工作量就会大为减少。原困每页的大小是每个字节的60 0倍。也不,内存中的总页数也不总字节数的四千分之一。对应关系也缩减为原始策略的四千分之一。分页让虚拟内存地址的设计有了实现的原困。

无论是虚拟页,还是物理页,一页之内的地址有的是连续的。原来 励志的话 ,六个 虚拟页和六个 物理页对应起来,页内的数据就能只能按顺序一一对应。这原困,虚拟内存地址和物理内存地址的末尾部分应该完整性相同。大多数情况报告下,每一页有4096个字节。原困4096是2的12次方,也不有地址最后12位的对应关系火山岩成立。当我门 把地址的你你你这个 部分称为偏移量(offset)。偏移量实际上表达了该字节在页内的位置。地址的前一部分则是页编号。操作系统只前要记录页编号的对应关系。



图2 地址翻译过程

多级分页表

内存分页制度的关键,在于管理多线程 池池空间页和物理页的对应关系。操作系统把对应关系记录在分页表(page table)中。你你你这个 对应关系让上层的抽象内存和下层的物理内存分离,从而让Linux能灵活地进行内存管理。原困每个多线程 池池会有一套虚拟内存地址,没办法 每个多线程 池池有的是有六个 分页表。为了保证查询带宽,分页表也会保处在内存中。分页表有也不有种实现最好的辦法 ,最简单的某种分页表也不把所有的对应关系记录到同六个 线性列表中,即如图2中的“对应关系”部分所示。

你你你这个 单一的连续分页表,前要给每六个 虚拟页预留两根记录的位置。但对于任何六个 应用多线程 池池,其多线程 池池空间真正用到的地址都相当有限。当我门 还记得,多线程 池池空间会有栈和堆。多线程 池池空间为栈和堆的增长预留了地址,但栈和堆很少会占满多线程 池池空间。这原困,原困使用连续分页表,也不有条目都没办法 真正用到。也不,Linux中的分页表,采用了多层的数据行态。多层的分页表也能减少所需的空间。

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



图3 多层分页表



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

多层分页表就好像把完整性的电话号码分成区号。当我门 把同一地区的电话号码以及对应的人名记录同通六个 小本子上。再用六个 上级本子记录区号和各个小本子的对应关系。原困某个区号没办法 使用,没办法 当我门 只前要在上级本子上把该区号标记为空。同样,一级分页表中0x01记录为空,说明了以0x01开头的虚拟地址段没办法 使用,相应的二级表就不前要处在。正是通过你你你这个 手段,多层分页表处在的空间要比单层分页表少了也不有。

多层分页表还有原来 优势。单层分页表前要处在于连续的内存空间。而多层分页表的二级表,能只能散步于内存的不同位置。原来 励志的话 ,操作系统就能只能利用零碎空间来存储分页表。还前要注意的是,这里多样化了多层分页表的也不有细节。最新Linux系统中的分页表多达3层,管理的内存地址也比本章介绍的长也不有。不过,多层分页表的基本原理有的是相同。

综上,当我门 了解了内存以页为单位的管理最好的辦法 。在分页的基础上,虚拟内存和物理内存实现了分离,从而让内核深度图参与和监督内存分配。应用多线程 池池的安全性和稳定性也不大为提高。

欢迎阅读“骑着企鹅采树莓”系列文章