虽然应用程序操作的对象是映射到物理内存之上的虚拟内存,但是处理器直接操作的却是物理内存。所以当用程序访问一个虚拟地址时,首先必须将虚拟地址转化成
物理地址,然后处理器才能解析地址访问请求。地址的转化工作需要通过查询也不才能完成,概括地讲,地址转换需要将虚拟地址分段,使每段虚拟地址都作为一个
索引指向页表,而页表则指向下一级别的页表或者指向最终的物理页面。
linux中使用三级页表完成地址转换。利用多级页表能够节约地址转换需要占用的存放空间。如果利用三级页表转换地址,即使是64位机占用的空间也有限。Linux对所有体系结构,包括那些不支持三级页表的体系结构都使用三级页表管理,因为使用三级页表结构可以利用“最大公约数”的思想。
顶级页表是页全局目录(PGD)。PGD包含了一个pgd_t类型数组,多数体系结构中pgd_t类型等同于无符号长整型类型。PGD中的页表项指向二级页目录中的表项:PMD。
二级页表是中间页目录(PMD)。PMD是个pmd_t类型数组,其中的表项指向PTE中的表项。
最后一级的页表称为页表,其中包含pte_t类型的页表项,该表项指向物理页面。
页表对应的结构体依赖于具体的体系结构,所以定义在asm/page.h中。
多数体系结构中,搜索页表的工作由硬件完成。虽然通常操作中,很多使用页表的工作都可以由硬件执行,但是只有在内核正确设置页表的前提下,硬件才能方便操作它们。

每个进程都有自己的页表(线程会共享页表)。内存描述符的pgd域指向的就是进程的页全局目录。操作和检索页表时必须使用page_table_lock锁,该锁在相应的进程的内存描述符中,以防止竞争条件。
由于几乎每次对虚拟内存中的页面访问都必须先解析它,然后得到物理内存中对应的地址,所以页表的操作性能很关键。为了加快搜索,多数体系结构实现了 TLB(translation lookside buffer)。TLB作为一个将虚拟地址映射到物理地址的硬件缓存,当请求访问一个虚拟地址时,处理器将首先检查TLB中十分缓存了该虚拟地址到物理地 址的映射,如果在缓冲中直接命中,物理地址立刻返回;未命中,就需要再通过页表搜索需要的物理地址。
2.6内核对页表管理的主要改进是:从高端内存分配部分页表。今后可能的改进包括通过在写时拷贝(copy-on-write) 的方式共享页表。这种机制似的在fork()操作中可由父子进程共享页表。因为只有当子进程或父进程试图修改特定页表项时,内核才去创建该页表项的新拷 贝,此后父子进程才不再共享该页表项。利用共享页表可以消除fork()操作中页表拷贝所带来的消耗。
linux中使用三级页表完成地址转换。利用多级页表能够节约地址转换需要占用的存放空间。如果利用三级页表转换地址,即使是64位机占用的空间也有限。Linux对所有体系结构,包括那些不支持三级页表的体系结构都使用三级页表管理,因为使用三级页表结构可以利用“最大公约数”的思想。
顶级页表是页全局目录(PGD)。PGD包含了一个pgd_t类型数组,多数体系结构中pgd_t类型等同于无符号长整型类型。PGD中的页表项指向二级页目录中的表项:PMD。
二级页表是中间页目录(PMD)。PMD是个pmd_t类型数组,其中的表项指向PTE中的表项。
最后一级的页表称为页表,其中包含pte_t类型的页表项,该表项指向物理页面。
页表对应的结构体依赖于具体的体系结构,所以定义在asm/page.h中。
- 在文件include/asm-i386/page.h中
- #ifdef CONFIG_X86_PAE
- extern unsigned long long __supported_pte_mask;
- typedef struct { unsigned long pte_low, pte_high; } pte_t;
- typedef struct { unsigned long long pmd; } pmd_t;
- typedef struct { unsigned long long pgd; } pgd_t;
- typedef struct { unsigned long long pgprot; } pgprot_t;
- #define pmd_val(x) ((x).pmd)
- #define pte_val(x) ((x).pte_low | ((unsigned long long)(x).pte_high << 32))
- #define __pmd(x) ((pmd_t) { (x) } )
- #define HPAGE_SHIFT 21
- #include <asm-generic/pgtable-nopud.h>
- #else
- typedef struct { unsigned long pte_low; } pte_t;
- typedef struct { unsigned long pgd; } pgd_t;
- typedef struct { unsigned long pgprot; } pgprot_t;
- #define boot_pte_t pte_t /* or would you rather have a typedef */
- #define pte_val(x) ((x).pte_low)
- #define HPAGE_SHIFT 22
- #include <asm-generic/pgtable-nopmd.h>
- #endif
多数体系结构中,搜索页表的工作由硬件完成。虽然通常操作中,很多使用页表的工作都可以由硬件执行,但是只有在内核正确设置页表的前提下,硬件才能方便操作它们。
Linux总是假定处理器有三级页表。每个页表通过所包含的下级页表的页面框号来访问。下图给出了虚拟地址是如何分割成多个域的,每个域提供了 某个指定页表的偏移。为了将虚拟地址转换成物理地址,处理器必须得到每个域的值。这个过程将持续三次直到对应于虚拟地址的物理页面框号被找到。最后再使用 虚拟地址中的最后一个域,得到了页面中数据的地址。
为了实现跨平台运行,Linux提供了一系列转换宏使得核心可以访问特定进程的页表。这样核心无需知道 页表入口的结构以及它们的排列方式。
这种策略相当成功,无论在具有三级页表结构的Alpha AXP还是两级页表的Intel X86处理器中,Linux总是使 用相同的页表操纵代码。
Linux的三级页表结构

每个进程都有自己的页表(线程会共享页表)。内存描述符的pgd域指向的就是进程的页全局目录。操作和检索页表时必须使用page_table_lock锁,该锁在相应的进程的内存描述符中,以防止竞争条件。
由于几乎每次对虚拟内存中的页面访问都必须先解析它,然后得到物理内存中对应的地址,所以页表的操作性能很关键。为了加快搜索,多数体系结构实现了 TLB(translation lookside buffer)。TLB作为一个将虚拟地址映射到物理地址的硬件缓存,当请求访问一个虚拟地址时,处理器将首先检查TLB中十分缓存了该虚拟地址到物理地 址的映射,如果在缓冲中直接命中,物理地址立刻返回;未命中,就需要再通过页表搜索需要的物理地址。
2.6内核对页表管理的主要改进是:从高端内存分配部分页表。今后可能的改进包括通过在写时拷贝(copy-on-write) 的方式共享页表。这种机制似的在fork()操作中可由父子进程共享页表。因为只有当子进程或父进程试图修改特定页表项时,内核才去创建该页表项的新拷 贝,此后父子进程才不再共享该页表项。利用共享页表可以消除fork()操作中页表拷贝所带来的消耗。