了解linux内存分页机制 (linux内存分页机制)

了解Linux内存分页机制

在操作系统中,内存的管理是一个非常重要的问题。在Linux的内存管理中,分页机制是非常核心的一部分。了解了Linux内存分页机制,可以让我们更好地理解Linux操作系统的工作原理,并同时有效地提升我们的系统维护与管理能力。

为什么需要分页机制?

在早期的操作系统中,内存的管理方式是直接映射,即将内存直接映射到物理存储器。但这种方式会存在一些问题。因为进程会调用大量的指令和数据,当内存资源不足时,使用直接映射方式时,就会出现无法分配内存的情况,而进程将无法正常运行。

分页机制则可以有效解决这个问题,它将内存划分成了等大小的数据块,即页,每一页在物理内存上也对应着不同的页框。当进程需要内存空间时,分页机制会自动调度物理内存上的空闲页框,为程序分配内存。这样,无论进程调用的指令和数据大小是多少,都可以只分配所需的空间大小,避免内存浪费。

什么是页表?

分页机制中,页和页框是一一对应的,每个页都需在物理内存上占据一个页框。为了建立内存的逻辑地址空间和物理地址空间之间一一对应关系,需要建立一个页表,它记录了每个页的物理地址对应的页框地址。页表通常是一个由操作系统维护的数据结构,可通过MMU硬件实现分页机制,并管理进程的内存。

页表通常由两个部分组成:一部分是页目录,另一部分是页表。页目录中记录着页表的物理地址,而页表中是一个映射关系表。通过页表,操作系统可以将进程的虚拟地址转换成物理地址,帮助CPU实现内存寻址的过程。

页表还可以实现内存保护的功能。在页表中,可以将某个页设置为只读的,防止程序误写该页的内容。也可以将某个页设置为一个不可访问的页,以防止程序读写该页的内容。

Linux分页管理方式

在Linux中,通过分页机制来管理进程的内存。通常情况下,Linux采用的是4KB大小的物理页框。对于每个进程,Linux都为其分配了一组独立的页表,用于将其虚拟地址空间映射到物理地址空间。

Linux中,页表的建立、更新、销毁都是由内核来完成的。当进程请求内存时,内核会根据页表为其分配物理页框并建立映射关系,如果内存不足,则会将一些物理页框交换到磁盘上进行缓存,从而为新进程腾出一些物理内存空间。同时,Linux内核还支持多种内存管理策略,如伙伴系统和slab分配器等。

在Linux中,分页机制是实现内存管理的核心技术之一。掌握Linux的内存分页机制,可以更加深入地理解Linux的内存管理原理,并通过编写程序和接受故障来维护和优化Linux系统。分页机制也是内存管理的基础,可以帮助我们更清晰地认识计算机硬件和操作系统内部的工作方式,对于开发者和系统管理员来说,了解分页机制是非常重要的。

相关问题拓展阅读:

Linux存储管理方式

这种方式中,将用户程序的地址空间,注意,是

用户程序的地址空间

分为若干个固定大小的区域,成为“页”或“页面”。我们可以知道,这也页其实是不存在的,只是一种划分内存空间的方法。也就是说,这种方式将用户的程序

“肢解”

了,分成很多个小的部分,每个部分称为一个“页”。

将逻辑地址的前n位作为页号,后面32-n位作为页内偏移量。

由于进程的最后一页经常装不满一个块,从而形成了不可利指洞用的碎片,称之为

“页内碎片”

作用:实现页号到物理号的地址映射。

页表是记录逻辑空间(虚拟内存)中每一页在内存中对应的物理块号。但并非每一页逻辑空间都会实际对应着一个物理块,只有实际驻留在物理内存空间中的页才会对应着物理块。

系统会为每一个进程建立一张页表,页表是需要一直驻留在物理内存中的(多级页表除外),另外页表的起址和长度存放在 PCB(Process Control Block)进程控制结构体中。

可以在页表的表项中设置相关的权限控制字段,例如设置存取控制字段,用于保护该存储块的读写;若存取控制字段为2位,则可以设置读/写、只读和只执行等存取方式。

物理块唯念枯是实实在在存在于内存中的:

由于执行频率高,要求效率比较高,需要使用硬件实现。

在系统中设置一个

页表寄存器(PTR)

,其中存放页表在内存的起始地址和页表的长度。平时进程未执行的时候,页表的起始地址和页表长度放在本进程的PCB中。当调度程序调度到某个进程的时候,才将这两个数据装入

页表寄存器

变换过程:

快表的变换机构

为了提高地址变换速度,可在地址变换机构中增设一个具有并行查询能力的特殊高速缓冲寄存器,又称为”联想寄存器”或者“快表”。俗称TLB。

快表与页表的功能类似,其实就是将一部分页表存到 CPU 内部的高速缓冲存储器 Cache。CPU 寻址时先到快表查询相应的页表项形成物理地址,如果查询不到,则到内存中查询,并将对应页表项调入到快表中。但,如果快表的存储空间已满,则需要通过算法找到一个暂时不再需要的页表项,将它换出内存。

由于成本的关系,快表不可能做得很大,通常只存放 16~512 个页表项,这对中、高亮小型作业来说,已有可能把全部页表项放在快表中;但对于大型作业而言,则只能将其一部分页表项放入其中。由于对程序和数据的访问往往带有局限性,因此,据统计,从快表中能找到所需页表项的概率可达 90% 以上。这样,由于增加了地址变换机构而造成的速度损失可减少到 10% 以下,达到了可接受的程度。

我们可以采用这样两个方法来解决这一问题:

① 对于页表所需的内存空间,可采用离散分配方式,以解决难以找到一块连续的大内存空间的问题;

只将当前需要的部分页表项调入内存,其余的页表项仍驻留在磁盘上,需要时再调入。

二级页表的页表项:

过程:

在采用两级页表结构的情况下,对于正在运行的进程,必须将其外层页表调入内存,而对于内页表则只需调入一页或几页。为了表征某页的页表是否已经调入内存,还应在外层页表项中增设一个状态位 S,其值若为 0,表示该页表分页不在内存中,否则说明其分页已调入内存。进程运行时,地址变换机构根据逻辑地址中的 P1去查找外层页表;若所找到的页表项中的状态位为 0,则产生一个中断信号,请求 OS 将该页表分页调入内存。

多级页表和二级页表类似。多级页表和二级页表是为了节省物理内存空间。使得页表可以在内存中离散存储。(单级页表为了随机访问必须连续存储,如果虚拟内存空间很大,就需要很多页表项,就需要很大的连续内存空间,但是多级页表不需要。)

为什么引入分段存储管理?

引入效果:

它将用户程序的地址空间分为若干个大小不同的的段,每个段可以定义一组完整的信息。

段号表示段名,每个段都从0开始编址,并且采用一段连续的地址空间。

在该地址结构中,允许一个作业最长有64K个段,每个段的更大长度为64KB。

在分段式存储管理系统中,为每一个分段分配一个连续的分区。进程的各个段,可以离散地装入内存中不同的分区中。

作用:实现从逻辑地址到物理内存区的映射。

为了保证程序能够正常运行,就必须能够从物理内存中找出每个逻辑段所对应的位置。为此在系统中会为每一个进程建立一张

段表

。每个段在表中有一个表项,其中记录了该段在内存中的起始地址和段的长度。一般将段表保存在内存中。

在配置了段表之后,执行的过程可以通过查找段表,找到每一个段所对应的内存区。

为了实现进程从逻辑地址到物理地址的变换功能,在系统设置了段表寄存器,用于存放段表的起始地址和段表长度TL。

在进行地址变换时,系统将逻辑地址中的段号与段表长度TL 进行比较。若 S > TL,表示段号太大,是访问越界,于是产生越界中断信号。若未越界,则根据段表的始址和该段的段号,计算出该段对应段表项的位置,从中读出该段在内存的起始地址。然后,再检查段内地址 d 是否超过该段的段长 SL。若超过,即 d>SL,同样发出越界中断信号。若未越界,则将该段的基址 d 与段内地址相加,即可得到要访问的内存。

分页和分段系统相似之处:两者都采用离散分配方式,且都是通过地址映射机构实现地址变换。

但在概念上两者完全不同,主要表现在下述三个方面:

分页系统以页面作为内存分配的基本单位,能有效地提高内存利用率,而分段系统以段作为内存分配的基本单位,它能够更好地满足用户多方面的需要。

段页式地址结构由段号、段内页号及页内地址三部分所组成

段页式系统的基本原理是分段和分页原理的结合,即先将用户程序分成若干个段,再把每个段分成若干个页,并为每一个段赋予一个段名。如下图展示了一个作业地址空间的结构。该作业有三个段:主程序段、子程序段和数据段;页面大小为 4 KB:

在段页式系统中,为了实现从逻辑地址到物理地址的变换,系统中需要同时配置段表和页表。段表的内容与分段系统略有不同,它不再是内存始址和段长,而是页表始址和页表长度。下图展示出了利用段表和页表进行从用户地址空间到物理(内存)空间的映射。

在段页式系统中,为了便于实现地址变换,须配置一个段表寄存器,其中存放段表始址和段长 TL。进行地址变换时,首先利用段号 S,将它与段长 TL 进行比较。若 S

在段页式系统中,为了获得一条指令或数据,须三次访问内存。之一次访问是访问内存中的段表,从中取得页表始址;第二次访问是访问内存中的页表,从中取出该页所在的物理块号,并将该块号与页内地址一起形成指令或数据的物理地址;第三次访问才是真正从第二次访问所得的地址中取出指令或数据。

显然,这使访问内存的次数增加了近两倍。为了提高执行速度,在地址变换机构中增设一个高速缓冲寄存器。每次访问它时,都须同时利用段号和页号去检索高速缓存,若找到匹配的表项,便可从中得到相应页的物理块号,用来与页内地址一起形成物理

参考链接:

《操作系统概念精要》之内存篇(三)-分页的页表结构

之前讨论了分段和分页,现在看下页表的主要涉及的页表结构。

大多数现代计算机系统支持大逻辑地址空间(2^32 ~ 2^64)。这种情况下,页表本身可以非常大。

例如:假如具有32位逻辑地址空间的一个计算机系统。如果系统的页大小为4KB(2^12)。那么页表可以多达100万的条目 (2^32/ 2^12)。假设某个项目有4字节。那么每个进程需要4MB的地址物理地址来存储页表本身。显然,我们并不想在内存中连续分配这么多页表。

这个问题的一个简单的解决方法就是讲页表划分为更小的块。完成这种划分方法有很多种。

最简单的方法就是使用两层分页算法,就是将页表再分页,例如,再次假设一个系统,具有32位逻辑地址空间和4K大小的页。一个逻辑地址被分为20位的页码和12位的页偏移。

因此要对20位的页表进行再分页,所以该页码可以分10位的页码和10位的偏移。这样一个逻辑地址就会分为如下表示。

其中p1表示的用来访问外部页表的索引,而p2是内部页表的页偏移。采用这种结构的地址转换方法。由于地址转换有外向内,所以这种也称为

向前映射页表

在这种分页结构的方案中,假设,系统是64位系统,那么当它的地址空间就有2^64, 当再以4KB作为地址的话,那么页表就会2^52个条目,那么就把页表进行细分,从而形成三级分层分页,四级分层分页等等。

为了装换每个逻辑地址,74位的系统需要7个级别的分页型败明,如此多的内存访问时不可取的,从而分层分页在64位的系统并不是更优的。

处理大于32位的地址空间的常用方法是

哈希页表

,采用虚拟页码作为哈希表值。哈希页表的每一个条目都包括一个链表,该链表的元素哈希到同意位置(这表示它们有了哈希冲突)。每个元素由三个字段组成:虚拟页码,映射的帧码,指向链表内下一个元素的指针。

该算法的工作如下:虚拟地址的虚拟页码哈希到哈希表。用虚拟页码与链表内的之一个元素的之一个字段相比较。如果匹配,那么相应的帧码(第二个字段)就用来形成物理地址。如果不匹配,那么与链表内的后续节点的之一个字段进行比较。以查找匹配的页码。该方案如图:

这里书上提到的虚拟页码可以只看作是页码。(之所以叫虚拟页码,是因为根据虚拟内存的概念,逻辑地址空间可以比物理地址大,所以多出来的部分被称为虚拟的,具体介绍会在下一章提到)。

已提出用于64位地址空间的这个方案的一个变体。

此变体采用

聚簇页表

类似于哈希页表。不过哈希表内的每个条目引用多个页而不是单个页。单个页表的条目可以映射到多个物理帧。聚簇页表对于

稀疏

地址空间特别有用。这里引用的是不连续的并且散布在整个地址空间。

通常,每个进程都有一个关联的页表。该进程所使用的每个页都在也表中有一项(或者每个虚拟页都有一项)。这种表示方法比较自然,因为进程是通过虚拟地址来引用页的。然后是操作系统将这些地址转换为物理内存地址。

由于页表是按照虚拟地址排序的,操作系统可计算所对应条目在页表的位置,可以直接使用该值。这种方法缺点就是:当每个页表包含百万级的数目时。会有性能问题,而且需要大量的内存来保存页表信息。

解决的方法处理上面的两种方法外,还有一种就是

倒置页表

这里先介绍一个IBM RT 的倒置页表的表示方法:

对于每个真正的内存页或者帧,倒置页表只有一个条目。每个条目包含

保存在真正内存位置上的页的虚拟地址

,以及拥有

该页的进程信息

。具体的过程如图:

这里的进程的信息就是以前提到的 空间地址标识符(ASID)。主要原因是由于一个倒置页表通常包含了多个不同的映射物理内存的地址空间。具体进程的每个逻辑页可映射相应的物理帧。

采用卜告倒置页表的系统在实现共享内存的时候会有问题,因为共享内存的实枯肆现为:将多个地址空间映射到同一个物理地址。这种方法,不能用于倒置页表,因为每个物理页只有一个虚拟的页条目,一个物理页不能有多个共享的虚拟地址。

IA-32 系统的内存管理可以分为分段和分页两个部分,工作如下:CPU 生成逻辑地址,并交给分段单元,分段单元为每个逻辑地址生成 一个线性地址。 然后线性地址交给分页单元,以生成内存的物理地址。

IA-32 架构允许一个段的大小最多可以达到4G, 每个进程最多有16K个段。进程的逻辑地址空间分为两部分。

之一部分最多由8K段组成,这部分是单个进程私有;

第二部分也是最多由8K段组成,这部分是所有进程共享。

之一部分保存在

局部描述符表(LTD)

中,第二部分保存在

全局描述符表(GDT)

中,他们的每个 条目都是8个字节,包括一个段的详细信息。比如段基地址和段界限。

逻辑地址一般为二元数组(选择器,偏移),选择器是一个16位的数:

其中s表示段号,g表示实在LTD中还是在GDT中, p表示保护信息。

段的寻址过程为:

IA-32架构的页可分为4K,或者4M 。采用4K的页,IA-32采用二级分页方法。其中的32位的寻址和表示请参照二级分页算法。

为了提高物理内存的使用率,IA-32 的页表可以被交换存在磁盘。因此,页目录的条目通过一个

有效位

,以表示该条目所指的页表实在内存还是在磁盘上。如果页表再磁盘上,则操作系统可通过其他31位来表示页表的磁盘位置。之后根据需要调入内存。

随着软件开发人员的逐步发现,32位架构的4GB内存限制,Inter通过

页地址扩展

,以便允许访问大于4GB的物理地址空间。

引入页地址扩展,主要是将两级的分页方案扩展到了三级方案, 后者的最后两位用于指向页目录指针表。

页地址扩展使得地址地址空间从32位增加到了36位。Linux和Mac OS X 都支持了这项技术。

X86-64 支持更大的逻辑和物理地址空间。支持64位的地址空间意味着可寻址的内存达到惊人的2^64字节。64位系统有能力访问那么多的内存,但是实际上,目前设计的地址远没有那么多。

目前提供的x86-64 架构的机器最多采用四级分页,支持48位的虚拟地址。它的页面大小可以4KB,2MB,或者1G。

虽然Intel的芯片占了大部分的市场,但是移动设备的架构一直采用的是32位ARM的架构。现在的iPhone 和iPad 都或得了ARM的授权。Android的智能手机也都是ARM的处理器。

ARM支持的页面大小:

ARM架构还支持两级TLB(高速缓存)。在外部,有两个微TLB: 一个用于数据,另一个用于指令。微TLB也支持 (ASID)进程地址空间标识符。 在内部 有一个主 TLB。 地址转换从微TLB级开始。如果没有找到,那么再检查主TLB。如果还没找到,再通过页表进行硬件查找。

关于linux内存分页机制的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。


数据运维技术 » 了解linux内存分页机制 (linux内存分页机制)