超越物理内存限制的内存管理机制

没有为止,我们讨论的都是基于一个重要假设:每个进程的地址空间都能被 load 进内存。

这个假设是不现实的,因为内存永远都有被用完的一天,就跟人的一生生一样,我们必须考虑在有限的空间和时间里,让更多进程得到内存被分配的机会。

交换空间 Swap Space

内核会保留一些磁盘用于把内存页换出去,我们通常把这些磁盘空间叫做 交换空间(swap space)

操作系统还必须记住这些被换出的内存页是属于哪个进程的,存放在磁盘的哪里。(具体的未在本节介绍)

有了磁盘空间的加持,操作系统现在可以给进程提供一种虚拟内存比物理内存大的“假象”了。

01

The Present Bit

这一位表示当前页在不在内存当中,如果不在,则触发 Page Fault,如果在则使用。

这一位存在 PTE 中。

The Page Fault

经过前面几章的介绍,我们应该能猜到 Page Fault 也是一个 trap 了,当发生 Page Fault 的时候操作系统要调用既定的代码来出这个 trap,它将决定哪个页被用来使用,发生了 TLB miss 怎么办,每一次都要挑选一个页换出内存么,IO 效率会不会太低了。

通常,处理这个 trap 可能也可以硬件来帮忙做,不过现代操作系统通常采取软件管理的方式来处理 Page Fault,因为交由硬件来处理太过复杂了。

如果内存满了怎么办?

在之前的例子中,我们只是简单的讨论这个把内存页换入换出的机制怎么实现,还是活在物理内存足够被外存的页换进来的假象中。

如果物理内存页满了怎么办?没办法,只能先把一页物理内存先交换到磁盘中区,那么操作系统该选择把哪一页换出去呢?

这个选择的策略,叫做 页替换策略(page-replacement policy)

如果策略做出了一个糟糕的决定,可能会拖累整个进程的运行速度,速度降低 10000~1000000 倍,因为不同存储层级的访问速度差距摆在那儿。

我们现在就知道必须有这么一种策略存在,然后在下一篇再详细介绍。

Page Fault 控制流程

硬件负责控制的流程

VPN = (VirtualAddress & VPN_MASK) >> SHIFT
(Success, TlbEntry) = TLB_Lookup(VPN)

if (Success == True)   // TLB Hit
    if (CanAccess(TlbEntry.ProtectBits) == True)
        Offset = VirtualAddress & OFFSET_MASK
        PhysAddr = (TlbEntry.PFN << SHIFT) | Offset
        Register = AccessMemory(PhysAddr)
    else
        RaiseException(PROTECTION_FAULT)
else
    PTEAddr = PTBR + (VPN * sizeof(PTE))
    PTE = AccessMemory(PTEAddr)
    if (PTE.Valid == False)
        RaiseException(SEGMENTATION_FAULT)
    else
        if (CanAccess(PTE.ProtectBits) == False)
            RaiseException(PROTECTION_FAULT)
        else if (PTE.Present == True)
            // assuming hardware-managed TLB
            TLB_Insert(VPN, PTE.PFN, PTE.ProtectBits)
            RetryInstruction()
        else if (PTE.Present == False)
            RaiseException(PAGE_FAULT)

软件负责的控制流

PFN = FindFreePhysicalPage()
if (PFN == -1)              // no free page found
    PFN = EvictPage()       // run replacement algorithm
DiskRead(PTE.DiskAddr, pfn) // sleep (waiting for I/O)
PTE.present = True          // update page table with present
PTE.PFN = PFN               // bit and translation (PFN)
RetryInstruction()          // retry instruction

当替换真的发生时

操作系统通常会维系一个空闲物理内存的标准,有个上界和下界。

当空闲内存数量低于下界时候,就触发一个后台线程,整理内存空间,淘汰内存页,直到空闲内存页数量达到上界要求的数量。

为了提高效率,操作系统通常不会触发一次 Page Fault 就进行一次换页操作,通常是一次换出很多页,这样做能减少IO,提高磁盘效率。

上面控制 Page Fault 的逻辑得稍微改下,不是直接执行换页策略,而是先检查有没有空闲的页可以被进程使用,如果没有的话,会唤起那个后台线程,等待后台线程完成 free pages 的任务,再唤醒当前线程来取页面。

那么问题来了,并发怎么控制呢?哈哈,这是这本书的另外一部分要重点介绍的内容了。

总结

操作系统提供的这种“假象”对于进程来说是透明的,缺点就是可能会非常耗时,减缓了进程的执行速度。这需要策略要仔细的实现。

引用
《Beyond Physical Memory: Mechanisms》

Comments
Write a Comment