linux内核页表更新

时间:2014-10-27 08:52:14

标签: linux x86 kernel paging

在linux x86中分页。

  1. 每个进程都有自己的页面目录。

  2. 页面表行走从CR3指向的页面目录开始。

  3. 每个进程共享内核页面目录内容

  4. 假设三个句子是正确的,让我们说一些过程进入内核 模式并更新他的内核页面目录内容(地址映射,访问 权利等......)

    问题。因为内核地址空间是在进程之间全局共享的, 此更新必须与其他进程的页面目录同步, 对?

    如何管理?

    提前谢谢。

3 个答案:

答案 0 :(得分:2)

我对Linux不了解,所以我将为Windows解答。某些内核空间是“全局”的,这是在PTE中设置的标志,指示该内核被多个进程使用。可以在寄存器操作数中配置INVPCID指令,以在TLB无效中包括或排除这些条目。这些页表条目在进程之间共享,并且所有条目都出现在每个进程的页表中的同一位置。这样,只需更新单个PTE,而无需同步其他进程的其他PTE,因为它们在物理地址上都共享一个PTE。

http://www.cs.miami.edu/home/burt/journal/NT/memory.html

某些内核内存并非对所有进程都可见,并且对每个进程都是私有的(不会改变它仍然是环0的事实)。在32位Windows系统上,其值为0xC0000000–0xC0200000,其中包含所有用户空间PTE和PDE,其中0xC0000000是PTE_BASE,它允许方程#define MiAddressToPe (x) ((PMMPTE)(((((ULONG)(x)) >> 12) << 2) + (ULONG_PTR)MmPteBase))优雅地用于转换{{1}中的错误虚拟地址}到PTE的地址。这是每个进程专用的,因为每个进程都有相同的基本PTE分配基地址;如果对所有进程可见,它将迅速占用虚拟内存,并且不必对所有进程可见,因为页面错误始终在当前进程的上下文中提供。

但是,用于为内核地址分配PTE的内核空间0xC0200000–0xC0400000将是全局的,并由所有进程共享。除了其中表示0xC0000000–0xC0200000的部分之外,根据我的计算,该部分将是0xC0300000–0xC0300800,这是PDE的用户模式端,如PDE_BASE = 0xC0300000–0xC0300FFF。

但是,不可能拆分PDE区域,因为它们将位于cr2所指向的同一物理帧上,并且cr3对于每个进程都是不同的,因此每个进程的所有PDE都是私有的这意味着必须在每个进程中复制并安装内核PDE。如果将内核页面表页面(包含内核页面表的页面)调出页面并移到新的物理位置,则所有PDE都必须同步。我不确定它是如何做到这一点的。

答案 1 :(得分:1)

  

问题。由于内核地址空间是进程之间全局共享的,因此此更新必须与其他进程的页面目录同步,对吗?

     

如何管理?

首先;了解分页通常是2个或更多级别的表。例如(对于80x86),对于最早的“纯32位分页”,有页表和页目录;对于当前的长模式,有页面映射级别4,页面目录指针表,页面目录和页面表。 CR3指向最高级别的表,并且每个虚拟地址空间(“进程”)的表必须不同。对于第二个最高级别的表,可以在所有所有最高级别的表中放入一个第二个最高级别的表,如果这样做,对第二个最高级别的表的任何更改都会自动更改每个虚拟地址空间。

这意味着(对于80x86),对于最早的“纯32位分页”,您可以将相同的“内核页表”放入所有虚拟地址空间(所有页目录)中,以及从中添加/删除页时页面表将自动影响所有虚拟地址空间;对于当前的长模式,可以将相同的页面目录指针表放在所有虚拟地址空间(所有页面映射级别4表)中,并且当添加/删除页面目录,页面表或页面时,它将自动影响所有虚拟地址空间。 / p>

这意味着您实际上只需要某种方法来更改第二个最高级别的页表(或某种方式来更改所有最高级别的页表条目)。有多种方法可以做到这一点。最简单的是预分配。例如,如果您说“内核空间将始终为N MiB”,则可以在启动过程中为“ N MiB”预先分配所有您需要的第二个最高级别的表,而不必更改它们(例如,对于长模式,您可以说该内核空间将为512 GiB,预先分配一个“内核页面目录指针表”,并在创建虚拟地址空间时将其放入每个页面映射级别4,然后依赖所有其他更改(到页面目录) ,页表等)自动影响所有虚拟地址空间的内核空间)。我相信这是Linux使用的方法(部分原因是Linux在启动时使用了愚蠢的“将所有RAM映射到内核空间”安全性灾难)。

但是,这仅仅是表更改。还有另外两个问题。

第一个“其他问题”是CPU的转换后备缓冲区(TLB); (从虚拟地址到物理地址)转换更改时,需要刷新该地址。大多数操作系统使用“惰性TLB击倒”的组合(CPU使用来自TLB的错误信息会导致页面错误,页面错误处理程序无效并返回,因此导致页面错误的软件可以继续进行新的/正确的转换,而无需知道发生任何事情)和“多CPU TLB击落”(将处理器间中断发送到其他CPU,并且该中断处理程序使TLB条目无效)。

第二个“其他问题”是确保CPU不要尝试同时更改同一事物。这通常最终是在更高层次上解决的问题。例如,如果您获取了某个数据结构的锁(在对该数据结构进行更改之前),并且意识到您需要为该数据结构分配/释放页面(在尝试进行更改时);那么修改分页表的代码就不必关心不同的CPU同时更改分页表了,因为它知道更高级别的操作(数据结构的锁)已经可以确保不会发生这种情况。

答案 2 :(得分:0)

当内核更改页表条目时,必须以原子方式进行这些更新:

  

在64位内核中,这可以使用64位内存操作方便地完成,而i386需要使用CMPXCHG8。

Source