华人澳洲中文论坛

热图推荐

    为何 Linux 需求虚构内存

    [复制链接]

    2022-11-20 07:12:38 22 0

    假如本文对你有帮忙,欢送关注、点赞、保藏、转发给敌人,让我有继续创作的能源
    操作零碎中的 CPU 和主内存(Main memory)都是稀缺资源,一切运转在以后操作零碎的过程会同享零碎中的 CPU 和内存资源,操作零碎会使用 CPU 调度器调配 CPU 时间1并引入虚构内存零碎以办理物理内存,本文会剖析操作零碎为何需求虚构内存。
    在回答虚构内存存在的须要性以前,咱们需求了解操作零碎中的虚构内存是甚么,它在操作零碎中起到甚么样的作用。正如软件工程中的其余笼统,虚构内存是操作零碎物理内存和过程之间的两头层,它为过程暗藏了物理内存这一律念,为过程提供了更为简洁和易用的接口以及更为繁杂的功用。



    图 1 - 过程和操作零碎的两头层
    假如需求咱们从头设计一个操作零碎,让零碎中的过程间接拜候主内存中的物理地址应该是十分天然的抉择,初期的操作零碎的确也都是这么完成的,过程会使用指标内存的物理地址(Physical Address)间接拜候内存中的内容,但是古代的操作零碎都引入了虚构内存,过程持有的虚构地址(Virtual Address)会通过内存办理单元(Memory Mangament Unit)的转换变为物理地址2,而后再经过物理地址拜候内存:



    图 2 - 虚构内存零碎
    主存储是相对于对比稀缺的资源,虽然程序读取只比磁盘快 1 个数量级,然而它能提供极快的随机拜候速度,从内存上随机读取数据是磁盘的 100,000 倍3,充沛利用内存的随机拜候速度是改良顺序履行效力的无效形式。
    操作零碎以页为单位办理内存,当过程发现需求拜候的数据不在内存时,操作零碎可能会将数据以页的形式加载到内存中,这个进程是由上图中的内存办理单元(MMU)实现的。操作零碎的虚构内存作为一个笼统层,起到了下列三个十分症结的作用:
    虚构内存能够利用内存起到缓存的作用,进步过程拜候磁盘的速度;虚构内存能够为过程提供独立的内存空间,简化顺序的链接、加载进程并经过静态库同享内存;虚构内存能够管制过程对物理内存的拜候,隔离不同过程的拜候权限,进步零碎的平安性;缓存咱们能够将虚构内存看做是在磁盘上一片空间,当这片空间中的一部份拜候对比频繁时,该部份数据会以页为单位被缓存到主存中以减速 CPU 拜候数据的机能,虚构内存利用空间较大的磁盘存储作为『内存』并使用主存储缓存进行减速,让下层以为操作零碎的内存很大并且很快,但是区域很大的磁盘其实不快,而很快的内存也其实不大



    图 3 - 虚构内存、主存和磁盘
    虚构内存中的虚构页(Virtual Page,VP)可能处于下列的三种形态 — 未调配(Unallocated)、未缓存(Uncached)和已缓存(Cached),其中未调配的内存页是没有被过程请求使用的,也就是闲暇的虚构内存,不占用虚构内存磁盘的任何空间,未缓存和已缓存的内存页分别表现仅加载到磁盘中的内存页和曾经加载到主存中的内存页。如上图所示,图中绿色的虚构内存页由主存中的物理内存页(Physical Page,PP)撑持,所以它是曾经缓存过的,而黄色的虚构内存页仅在磁盘中,所以没有被物理内存缓存。
    当用户顺序拜候未被缓存的虚构页时,硬件就会触发缺页间断(Page Fault,PF),在部份状况下,被拜候的页面曾经加载到了物理内存中,然而用户顺序的页表(Page Table)其实不存在该对应瓜葛,这时候咱们只需求在页表中建设虚构内存到物理内存的瓜葛;在其余状况下,操作零碎需求将磁盘上未被缓存的虚构页加载到物理内存中4。



    图 4 - 虚构内存的缺页间断
    由于主内存的空间是无限的,当主内存中不包孕能够使用的空间时,操作零碎会从选择适合的物理内存页摈除回磁盘,为新的内存页让出地位,选择待摈除页的进程在操作零碎中叫做页面交换(Page Replacement)。缺页间断和页面交换技术都是操作零碎调页算法(Paging)的一部份,该算法的目的就是充沛利用内存资源作为磁盘的缓存以进步顺序的运转效力。
    内存办理
    虚构内存能够为正在运转的过程提供独立的内存空间,制作一种每个过程的内存都是独立的假象,在 64 位的操作零碎上,每个过程都会具有 256 TiB 的内存空间,内核空间和用户空间分别占 十二8 TiB5,部份操作零碎使用 57 位虚构地址以提供 十二8 PiB 的寻址空间6。由于每个过程的虚构内存空间是彻底独立的,所以它们均可以残缺地使用 0x0000000000000000 到 0x00007FFFFFFFFFFF 的整个内存。



    图 5 - 操作零碎的虚构内存空间
    虚构内存空间只是操作零碎中的逻辑构造,就像咱们下面说的,运用顺序终究仍是需求拜候物理内存或者磁盘上的内容。由于操作零碎加了一个虚构内存的两头层,所以咱们也需求为过程完成地址翻译器,完成从虚构地址到物理地址的转换,页表是虚构内存零碎中的首要数据构造,每一个个过程的页表中都存储了从虚构内存到物理内存页的映照瓜葛,为了存储 64 位操作零碎中 十二8 TiB 虚构内存的映照数据,Linux 在 2.6.10 中引入了四层的页表辅佐虚构地址的转换7,在 4.十一 中引入了五层的页表构造8,在将来还可能会引入更多层的页表构造以反对 64 位的虚构地址。



    图 6 - 四层页表构造
    在如上图所示的四层页表构造中,操作零碎会使用最低的 十二 位作为页面的偏移量,剩下的 36 位会分四组分别表现以后层级在上一层中的索引,一切的虚构地址均可以用上述的多层页表查找到对应的物理地址。
    由于有多层的页表构造能够用来转换虚构地址,所以多个过程能够经过虚构内存同享物理内存。咱们在 为何 Redis 快照使用子过程 一文中引见的写时复制就利用了虚构内存的这个特性,当咱们在 Linux 中调用 fork 创立子过程时,实际上只复制了父过程的页表。如下图所示,父子过程会经过不同的页表指向相反的物理内存:



    图 7 - 过程间同享内存
    虚构内存不只能够在 fork 时用于同享过程的物理内存,提供写时复制的机制,还能同享一些常见的静态库增加物理内存的占用,一切的过程均可能调用相反的操作零碎内核代码,而 C 言语顺序也会调用相反的规范库。
    除了可以同享内存以外,独立的虚构内存空间也会简化内存的调配进程,当用户顺序向操作零碎请求堆内存时,操作零碎能够调配几个延续的虚构页,然而这些虚构页能够对应到物理内存中不延续的页中。
    内存维护
    操作零碎中的用户顺序不该该修正只读的代码段,也不该该读取或者修正内核中的代码和数据构造或者拜候公有的以及其余的过程的内存,假如无奈对用户过程的内存拜候进行限度,攻打者就能拜候和修正其余过程的内存影响零碎的平安。
    假如每一个个过程都持有独立的虚构内存空间,那末虚构内存中页表能够了解成过程和物理页的『衔接表』,其中能够存储过程和物理页之间的拜候瓜葛,包罗读权限、写权限和履行权限:



    图 8 - 读权限、写权限和履行权限
    内存办理单元能够抉择以后过程是不是有权限拜候指标的物理内存,这样咱们就终究将权限办理的功用整个收敛到虚构内存零碎中,增加了可能泛起危险的代码门路。
    总结
    虚构内存的设计办法能够说是软件工程中的常见伎俩,经过结合磁盘和内存各自的劣势,利用两头层对资源进行更公道地调度充沛进步资源的利用率并提供调和以及一致的笼统,而在实际的业务场景中,相似的缓存逻辑也对比常见。
    操作零碎的虚构内存是十分繁杂的组件,没有工程师可以理解其中的整个细节,不外理解虚构内存的总体设计也颇有价值,咱们可以从中找到得多软件设计的办法。咱们从新回到明天的问题 — Linux 操作零碎中为何需求虚构内存:
    虚构内存能够结合磁盘和物理内存的劣势为过程提供看起来速度足够快而且容量足够大的存储;虚构内存能够为过程提供独立的内存空间并引入多层的页表构造将虚构内存翻译成物理内存,过程之间能够同享物理内存增加开消,也能简化顺序的链接、装载以及内存调配进程;虚构内存能够管制过程对物理内存的拜候,隔离不同过程的拜候权限,进步零碎的平安性;到最初,咱们仍是来看一些对比凋谢的相干问题,有兴致的读者能够子细思考一下上面的问题:
    为何每层的页表构造只可以担任 9 位虚构地址的寻址?64 位的虚构内存在操作零碎中需求多少层的页表构造能力寻址?假如对文章中的内容有疑难或者想要理解更多软件工程上一些设计决策面前的缘故,能够在博客上面留言,作者会及时回复本文相干的疑难并选择其中适合的主题作为后续的内容。

    发表回复

    您需要登录后才可以回帖 登录 | 立即注册

    返回列表 本版积分规则

    :
    中级会员
    :
    论坛短信
    :
    未填写
    :
    未填写
    :
    未填写

    主题42

    帖子49

    积分230

    图文推荐