目录
我们在页表中,得到的是一个字节的地址,操作系统怎么知道我们要访问几个字节?
子进程如何修改父进程复制过来的页表对应的数据,不是已经设置成只读了吗:
如果虚拟地址映射的物理地址没有数据。操作系统是怎么从磁盘中加载数据到内存的呢
页表(重中之重)
页表是操作系统内存管理的一个重要部分,它用于实现虚拟内存机制。在大多数现代计算机系统中,操作系统使用虚拟内存来提供比物理内存更大的地址空间,以及更好的内存保护机制。
页帧(Page Frame)和页框(Page Frame)
页帧(Page Frame)和页框(Page Frame)在计算机系统中是指相同的概念,它们都是指内存中的固定大小的存储块。它们的主要作用是用来存储虚拟地址空间中的页面(Page)。
在内存管理中,虚拟内存被划分为固定大小的页面,通常为4KB或者8KB。而物理内存也被划分为与虚拟内存相同大小的页帧或页框。每个页面在内存中有一个相对应的页帧或页框。
当进程在运行过程中需要访问某个虚拟地址时,操作系统通过页表将虚拟地址转换为对应的物理地址。页表的一项记录了虚拟页面和物理页帧之间的映射关系。通过这种映射,可以实现虚拟内存的页面与物理内存的页帧之间的关联。
总之,页帧和页框是指物理内存中的固定大小的存储块,用来存放虚拟内存中的页面。它们的概念基本相同,只是在不同的文献或不同的背景下使用的术语可能会有所不同。
页表的存储:
页表存储的位置存储是物理内存:但它也可以被部分或全部存储在高速缓存(如CPU缓存),以提高访问速度。但是,页表的大小通常比较大,存储在PCB中可能会增加PCB的大小和访问时间,因此也有一些操作系统选择将页表存储在内存中,通过索引访问,而不是存储在PCB中。PCB中会有访问页表的实现方法。
-
页表项:
每个页表项(Page Table Entry, PTE)通常包含以下信息:-
其他信息:例如页大小、访问权限等。
-
有效位(Valid Bit):指示页表项是否有效,即对应的虚拟页面是否存在于物理内存中。
-
修改位(Dirty Bit):如果该页被修改过,修改位会被设置。
-
访问位(Access Bit):用于指示该页是否被访问过,常用于页面置换算法。
-
帧号(Frame Number):页表项中存储的对应虚拟页面的物理内存帧号。
-
-
页表的大小:
页表的大小取决于虚拟地址空间的大小和页大小。例如,如果一个系统的虚拟地址空间是4GB,且页大小是4KB,那么将需要1MB的页表来存储所有的页表项。(后文详细介绍) -
多级页表:
对于非常大的虚拟地址空间,使用多级页表结构来减少页表项占用的物理内存。在这种结构中,每一级页表指向下一级页表,最终指向物理内存中的帧。 -
页表的更新:
-
当一个虚拟地址映射到物理地址时,操作系统会更新页表中的相应项。
-
如果页面在物理内存中已被修改,相关的页表项的修改位会被设置。
-
-
页表访问:
-
在cpu中有专门的寄存器(cr3)来存储页表的地址,当进程离开cpu时会将页表一起带走(页表也是进程上下文的一部分)。
-
当执行内存访问指令时,处理器会使用这个寄存器来查找相应的页表项,以确定虚拟地址对应的物理地址。
-
注意:
-
页表是进程管理和内存管理之间的纽带,将进程管理模块和内存管理模块进行解耦合。
-
这里的解耦合:
解耦合进程管理和内存管理的方法主要有以下几种:
-
虚拟地址空间:虚拟地址空间是进程管理和内存管理解耦合的关键。每个进程都有自己的虚拟地址空间,可以独立地访问和管理自己的内存。这样可以实现进程之间的隔离和保护,避免进程之间的数据泄露和破坏。
-
独立的内存管理器:将进程管理和内存管理模块解耦可以通过为每个进程创建一个独立的内存管理器来实现。每个内存管理器负责管理该进程的虚拟地址空间和物理内存,这样可以实现进程之间的独立性和隔离性。
-
共享内存:在某些情况下,进程之间可能需要共享一些数据。为了实现进程之间的通信和协作,可以使用共享内存技术。通过将一些内存区域映射到多个进程的虚拟地址空间中,可以实现进程之间的数据共享和通信。
-
-
-
页表将无序变为有序:通过连续的空间,把处于磁盘上的各种切片信息整理起来。
页表中:页表项
帧号 | 物理内存页 | 状态位 | 其他属性 |
---|---|---|---|
虚拟地址 | 物理地址 | W?R? | 页面大小、指针 |
ox7777ffffff | 0x123456 |
页表(Page Table)中存储的是页表项(Page Table Entry):
页表项(Page Table Entry, PTE)是页表中的一个条目,它用于记录虚拟地址到物理地址的映射关系以及其他与内存管理相关的信息。每个页表项对应一个虚拟页面,虚拟页面是虚拟内存中的一个基本单位,通常情况下,每个虚拟页面的大小与物理页面的大小相同。以下是一个典型的页表项可能包含的信息:
每个页表项大小是四个字节
-
帧号(Frame Number):这是页表项中最关键的信息,它指出了虚拟页面在物理内存中的位置。当处理器访问虚拟内存时,操作系统通过查找页表项中的帧号来确定对应的物理地址。
-
有效位(Valid Bit):这个位用来指示虚拟页面是否存在于物理内存中。如果有效位被设置(通常是1),表示对应的虚拟页面有一个有效的物理帧;如果有效位未被设置(通常是0),则表示虚拟页面不在物理内存中,可能会触发一个页面置换操作。
-
修改位(Dirty Bit):当处理器对虚拟页面进行写操作时,修改位会被设置。这告诉操作系统,如果该页面被置换出物理内存,它的内容需要被写回磁盘。
-
访问位(Access Bit):每当处理器访问一个虚拟页面时,访问位会被设置。一些页面置换算法使用这个位来决定哪个页面应该被置换出物理内存。
-
页大小(Page Size):这个信息指示了虚拟页面和物理页面的大小。例如,如果页大小是4KB,那么每个虚拟页面包含4KB的数据。
-
访问权限(Access Permissions):页表项还可以包含访问权限信息,指示哪些进程或用户可以访问该页面。
-
其他信息:一些系统可能还在页表项中包含其他信息,如页面的使用情况、保留位等。
-
页表项的日期和过期时间:这可以帮助操作系统判断页面是否仍然有效,以及何时可能需要替换或更新页面。
页表项的具体内容和布局取决于操作系统的设计和硬件架构。在多级页表结构中,每一级的页表项可能包含指向下一级页表的指针,这样处理器可以通过多次查找来定位最终的物理地址。
在linux中32位架构下,我们采用多级页表结构:
多级页表结构
多级页表结构是现代计算机系统中常用的一种内存管理技术,它通过多层的页表来管理虚拟地址到物理地址的映射。这种结构允许系统支持非常大的虚拟地址空间,同时减少了页表项占用的物理内存空间。
在多级页表结构中,每一级的页表都包含指向下一级页表的指针,以及一些其他的信息,如页面的帧号或者更多的控制位。这种结构通常被称为“分页机制”(paging hierarchy)或者“虚拟存储器层次结构”(virtual memory hierarchy)。
多级页表结构的关键特点:
-
分页机制:虚拟地址空间被划分成固定大小的虚拟页面,物理内存也被划分成同样大小的物理页面。每一级的页表项指向级页表或者直接指向物理内存中的帧。
-
减少页表项的数量:通过多级页表,可以大大减少所需的页表项数量。例如,一个拥有4GB虚拟地址空间的平台,如果页面大小是4KB,那么将需要1MB的页表来存储所有的页表项。使用两级页表结构,可以通过索引的方式将页表项的数量减少到1KB。
-
快速的地址转换:处理器通常包含一个快速访问的页表寄存器,它存储了最顶级的页表的物理地址。当处理器访问虚拟内存时,它首先在页表寄存器中查找帧号,然后通过帧号快速访问物理内存。
-
灵活的地址转换:多级页表结构允许操作系统灵活地管理虚拟地址到物理地址的映射。例如,操作系统可以选择在某些情况下直接使用物理地址,或者在需要时进行页表更新。
-
页表项的内容:除了帧号,每一级的页表项可能还包括其他的信息,如有效位、修改位、访问位等,这些信息帮助操作系统管理内存的使用和优化性能。
举例说明:
假设一个系统使用4GB的虚拟地址空间,页面大小为4KB。 without分页机制,需要1MB的页表项来管理所有的虚拟页面。但是,通过使用两级页表结构,可以减少所需的页表项数量。
-
第一级页表:包含1KB的页表项,每个项指向一个第二级的页表。
-
第二级页表:包含4KB的页表项,每个项指向一个物理帧。
当处理器访问一个虚拟页面时,它首先在第一级页表中查找对应的第二级页表的物理地址,然后在该页表中查找帧号,最后访问物理内存。
页表项中的帧号
帧号(Frame Number)是页表项中的一个关键信息,它标识了虚拟页面在物理内存中的位置。每个帧号唯一地对应于物理内存中的一个帧,而每个帧通常包含多个连续的内存单元,这些内存单元组成了一个虚拟页面。
例如在4GB内存中,一共会有1024*1024张页,所以需要20个比特位来表示帧号。
在多级页表结构中,帧号可能是指向下一级页表的指针,而不是直接的物理地址。
页目录表中:页目录表项
在页目录表(Page Directory)中,每个页目录表项(Page Directory Entry,PDE)用于描述一个虚拟页面对应的物理页面的存储位置和其他属性。在32位系统中,页目录表项的大小通常是4字节(32位)。
每个页目录表项包含以下信息:
-
页表基址(Page Table Base Address):PDE中的页表基址存储了指向该虚拟页面对应的表(Page Table)的物理地址。页表包含了该虚拟页面对应的物理页面的详细信息,如物理页面的基址、访问权限等。
-
页面大小(Page Size):PDE中的页面大小指示了该虚拟页面的大小,通常与操作系统中设置的页面大小相同。例如,在Linux系统中,页面大小通常为4KB。
-
访问权限(Access Permissions):PDE中的访问权限指示了该虚拟页面可以被哪些进程访问。访问权限通常由操作系统分配和管理。
-
缓存属性(Cache Attributes):PDE中的缓存属性指示了该虚拟页面在处理器缓存中的存储行为。例如,是否允许在缓存中存储该页面的副本,或者在缓存中是否需要进行写穿(Write-through)操作等。
-
其他属性(Other Attributes):PDE中还可以包含其他与内存管理相关的属性,如页面是否已被换出(Paged Out)等。
在32位系统中,页目录表项的索引(类似数组的下标)通常由虚拟地址中的高10位组成。使用页目录表项中的信息,操作系统可以计算出对应的页表基址,并使用该基址来查找页表。在页表中,可以找到虚拟页面对应的物理页面的详细信息,如物理页面的基址、访问权限等。通过将这些信息组合起来,操作系统可以将虚拟地址转换为物理地址,并实现虚拟内存的管理。
二级页表结构的访问内存
在32位架构下使用二级页表结构的访问内存的过程如下所示:
假设系统的虚拟地址空间大小为4GB,页大小为4KB,使用二级页表结构。
-
从32位的虚拟地址中提取出以下三部分:
-
前10位:用作第一级页表(称作页目录)的索引(称为第一级索引),转换成十进制数就是数组的下标。
-
接下来的10位:用作第二级页表的索引(称为第二级索引)。
-
最后的12位:用作页面内的偏移量。
-
-
使用第一级索引从第一级页表中查找到对应的第二级页表,获取第二级页表的物理地址。第一级页表的起始地址通常存储在操作系统的页表寄存器中。
-
使用第二级索引从第二级页表中查找到对应的页表项,获取帧号(Frame Number)。每个页表项的大小为4字节。
-
计算物理地址:
-
将帧号左移12位(因为页大小为4KB,2^12 = 4KB),得到帧号的物理地址部分。
-
将页面内的偏移量拼接到物理地址中,得到最终的32位物理地址。
-
-
处理器使用物理地址访问物理内存,读取或写入数据。
首先得到32位的虚拟地址,与一级页表(页目录表)的地址。我们通过虚拟地址前十位,在一级页表中找到对应的页目录表项。在页目录表项存储着页表基址,通过后十位与这个地址,找到正确的页表项。在这个页表项中,帧号有20位,存储的是到物理内存页的映射关系,其他位存储一些其他信息。接着,将帧号与虚拟地址中的低12位组合,以计算出物理地址(起始地址+偏移量)。
需要注意的是,在实际的系统中,还可能采用更多级的页表结构来管理更大的地址空间或支持更大的页大小。但步骤的基本原理和流程保持不变,只是涉及到更多级别的索引和页表。
此外,考虑到性能方面的考虑,处理器通常会使用高速缓存(如TLB)来缓存最常访问的页表项,以减少对主存的频繁访问。因此,在实际场景中,可能会先在TLB中查找虚拟地址对应的物理地址,如果TLB命中,则直接使用其中的缓存结果进行访问,否则才会进行页表的访问和查找。
怎么统计页面的使用情况
为了在虚拟内存系统中优化对页面的访问和置换,需要对页面的使用情况进行统计和跟踪。页表项中可以加入访问位和修改位,这两个位通常用于记录虚拟页面的访问和修改情况,以便系统根据这些信息来进行页面置换及其他内存管理决策。统计页面的使用情况,可以通过以下两种方式:
-
访问位(Access Bit):页表项中的访问位可以用于记录一个页面是否被访问过。当处理器访问一个页面时,访问位会被设置为1,并在一段时间后复位为0。页面置换算法使用访问位来识别哪些页面最近被访问过,以便进行页面置换。
-
修改位(Dirty Bit):页表项中的修改位可以用于记录一个页面是否已经被修改过。当处理器对一个页面进行写操作时,修改位将被设置为1。当页面被置换出物理内存时,修改位会被读取,以决定是否需要将内容写回磁盘。此外,修改位还可以用于页面置换算法,例如Clock页面置换算法中,只有已经修改过的页面才会被选择作为替换页面。
操作系统可以定期扫描页表项中的访问位和修改位,并根据这些信息来统计页面的使用情况。例如,操作系统可以计算出每个页面被访问的次数,以及每个页面被修改的次数等。这些统计数据可以用于优化内存管理,例如选择更合适的页面置换算法、或者通过对页面的访问模式进行分析,优化页面缓存等。
虚拟页号和物理页号
在分页系统中,有两种页号的映射关系:虚拟页号(Virtual Page Number,VPN)和物理页号(Physical Page Number,PPN)。虚拟页号是进程中使用的虚拟地址中的页号部分,而物理页号是物理内存中实际页面的页号。
页号的映射是通过页表(Page Table)来实现的。页表是一种数据结构,存储了虚拟页号到物理页号之间的映射关系。在分页系统中,每个进程都有自己独立的页表,用于将进程的虚拟地址转换为对应的物理地址。
当进程访问一个虚拟地址时,操作系统会根据该虚拟地址中的虚拟页号,查找进程的页表,获取对应的物理页号。这个物理页号指示了在物理内存中的实际页面。
虚拟页号的范围通常较大,可以超过物理内存的大小。为了解决这个问题,操作系统使用了页面置换算法(Page Replacement Algorithm),以及其他技术(如内存分页和分段结合)来管理虚拟页号到物理页号的映射,以最大程度地利用有限的物理内存,并允许进程的页面在磁盘和内存之间进行动态迁移。
总之,虚拟页号和物理页号的映射关系由进程的页表确定。通过页表的查询,虚拟页号可以被映射到对应的物理页号,从而确定物理内存中的实际页面位置。
思考(重要):
有没有可能虚拟地址映射的物理地址还没有被分配空间
是的,有可能在虚拟地址映射的物理地址没有被分配空间。这种情况通常发生在以下几种情况下:
-
延迟分配:操作系统采用延迟分配策略时,虚拟地址可能先于物理地址分配。在这种情况下,当进程首次访问虚拟地址所指向的数据时,操作系统才会分配对应的物理内存空间,并将虚拟地址与物理地址建立映射。
-
页面错误:如果进程访问的虚拟地址所映射的物理地址尚未分配(页目录表项对应的页表项没有建立),或者物理页未加载到内存(缺页中断)中,会触发页面错误(Page Fault)。页面错误通常由操作系统处理,操作系统会将虚拟地址所需的数据从磁盘上加载到内存中,并更新页表以建立映射关系。通过这种方式,虚拟地址最终映射到了物理地址空间。
需要注意的是,虚拟地址空间是比物理地址空间更为广阔的概念。在实际的内存中,可能仅有部分虚拟地址被分配了物理内存空间,而其他虚拟地址可能没有相应的物理内存映射。当进程访问这些尚未映射的虚拟地址时,就会发生页面错误,操作系统会根据需要将相应的数据加载到内存中。
我们在页表中,得到的是一个字节的地址,操作系统怎么知道我们要访问几个字节?
确实,在虚拟内存系统中,页表项只提供了物理帧号并不确定具体的字节地址。操作系统和程序需要结合虚拟地址的使用方式和数据类型来确定要访问的字节数量。
对于CPU有不同的寄存器,可以一次读取多个字节例如(1,2,4,8,16),对于不同大小的类型,在汇编语句中会调用不同的寄存器,保证了访问数据的准确性。
对于自定义的类,在汇编过后,所有的类都是一系列内置类型的集合。所以空类的大小为什么是1?就是这个道理。
子进程的页表和父进程相同吗:
在fork()
函数中,子进程会复制一份父进程的页表,但是子进程的页表和父进程的页表并不完全相同。
当fork()
函数被调用时,操作系统会为新进程分配一个新的进程标识符(PID)并创建PCB,在PCB中指向一个与父进程相同的虚拟地址空间。然后,新进程会复制一份父进程的页表,并将自己的权限信息(读/写/只读)添加到页表中。
由于新进程和父进程有不同的权限,因此它们对内存的访问方式也不同。父进程只能读取自己的页表,而子进程可以读取自己的页表和父进程的页表。子进程的页表中包含了父进程页表中所有页面的副本,但是这些页面的权限信息已经被修改为子进程自己的权限信息。
子进程如何修改父进程复制过来的页表对应的数据,不是已经设置成只读了吗:
如果要进行写操作,或者父进程要对自己原本的数据进行修改,操作系统会捕获这一事件,并为正在进行写操作的进程复制相应的内存页,创建一个新的物理页并让该进程的虚拟地址映射指向这个新页。这样,父进程和子进程之间的数据就分开了,不再相互共享。(在后续进程创建中的写实拷贝中重点介绍)
这个知识点没有那么简单,在后续的学习会逐渐理解。
我自己目前的理解,因为有标志位记录原本父进程数据是只读还是读写,子进程为要对原本可写的数据进行操作就能被操作系统知道,这时就会发生物理内存页的复制,父子进程页表的虚拟地址是自身的属性,映射的物理地址才是关键,所以也出现了一样的虚拟地址不同的物理地址
父子进程写时拷贝的实现**
(在进程创建中介绍)
如果虚拟地址映射的物理地址没有数据。操作系统是怎么从磁盘中加载数据到内存的呢
(在文件系统后的文件的写入与写回中介绍)
拓展
大页存储结构
在计算机系统中,大页(Large Page)是一种内存管理技术,它允许操作系统将较大的内存块划分成更大的页面。这种技术可以减少页表项的数量,从而降低内存碎片化,提高内存利用率。
大页(也称为超级页面)存储结构通常在以下情况下有益:
-
减少页表项数量:在传统的分页系统中,每个虚拟页面都需要一个页表项来映射到物理页面。当页面大小较小时,例如4KB,一个进程可能需要大量的页表项来管理整个虚拟地址空间。大页可以将多个小页面合并成一个较大的页面,从而减少页表项的数量。
-
减少内存碎片:小页面会导致内存碎片化,因为随着进程的运行,它们可能会留下许多小的未使用内存块。大页可以减少这种碎片化,因为它们覆盖了更大的内存区域。
-
提高内存利用率:大页可以更好地利用内存空间,因为它们减少了内存中的空闲碎片。这对于内存密集型应用程序尤其重要,因为它们可以更有效地使用内存资源。
-
提高TLB命中率:TLB(Translation Lookaside Buffer)是一个高速缓存,用于存储最近使用的虚拟页面到物理页面的映射。大页可以增加TLB缓存的大小,从而提高TLB命中率,减少对主存页表的访问。
大页的实现通常涉及到操作系统的内存管理子系统。操作系统需要支持大页的分配和管理,并且需要在页表结构中进行相应的调整。在支持大页的系统中,每个大页仍然有一个页表项,但是这个页表项映射的是一个较大的内存块。
具体实现大页存储结构的方式因操作系统和处理器的不同而有所差异。以下是一种常见的实现方式:
-
大页分配:操作系统将一部分物理内存以大页的形式进行分配,而不是传统的小页面分配方式。大页的大小通常为2MB或4MB,并由处理器支持。
-
大页表项:在页表中,每个大页表项代表一个大页,它指向该大页在物理内存中的起始位置。相比传统的页表项,大页表项可以减少页表项的数量,从而节省了内存资源。
-
地址转换:当处理器访问虚拟地址时,操作系统首先通过查找大页表项来确定该虚拟地址所对应的大页的物理地址。然后,通过加上页内偏移量,计算出最终的物理地址。
例如,如果系统支持2MB的大页,那么每个大页会有一个页表项,这个页表项包含指向2MB内存块的物理。当处理器访问一个虚拟地址时,它首先会使用页表项中的信息找到对应的大页,然后在这个大页内使用偏移量来访问特定的内存位置。
大页结构是一种有效的内存管理技术,可以提高内存使用效率,减少内存碎片,并提高处理器的性能。