2.5.1 页相关的数据结构及宏的定义

     上一节讨论的分页机制是硬件对分页的支持,这是虚拟内存管理的硬件基础。要想使这种硬件机制充分发挥其功能,必须有相应软件的支持,我们来看一下Linux所定义的一些主要数据结构,其分布在include/asm-i386/目录下的page.hpgtable.hpgtable-2level.h三个文件中。

 1. 表项的定义

   如上所述,PGDPMDPT表的表项都占4个字节,因此,把它们定义为无符号长整数,分别叫做pgd_tpmd_tpte_t(pte Page table Entry),在page.h中定义如下:

    typedef struct { unsigned long pte_low; } pte_t;

    typedef struct { unsigned long pmd; } pmd_t;

typedef struct { unsigned long pgd; } pgd_t;

    typedef struct { unsigned long pgprot; } pgprot_t;

    可以看出,Linux没有把这几个类型直接定义长整数而是定义为一个结构,这是为了让gcc在编译时进行更严格的类型检查。另外,还定义了几个宏来访问这些结构的成分,这也是一种面向对象思想的体现:

   #define pte_val(x)      ((x).pte_low)

   #define pmd_val(x)     ((x).pmd)

   #define pgd_val(x)      ((x).pgd)

      从图2.22和图2.24 可以看出,对这些表项应该定义成位段,但内核并没有这样定义,而是定义了一个页面保护结构pgprot_t和一些宏:

   typedef struct { unsigned long pgprot; } pgprot_t;

   #define pgprot_val(x)   ((x).pgprot)

  字段pgprot的值与图2.24页面项的低12位相对应,其中的9位对应09位,在pgtalbe.h中定义了对应的宏:

  #define _PAGE_PRESENT   0x001  

  #define _PAGE_RW        0x002

  #define _PAGE_USER      0x004

  #define _PAGE_PWT       0x008

  #define _PAGE_PCD       0x010

  #define _PAGE_ACCESSED  0x020

  #define _PAGE_DIRTY     0x040

  #define _PAGE_PSE       0x080   /* 4 MB (or 2MB) page, Pentium+, if present.. */

  #define _PAGE_GLOBAL    0x100   /* Global TLB entry PPro+ */

    在你阅读源代码的过程中你能体会到,把标志位定义为宏而不是位段更有利于编码。

    另外,页目录表及页表在pgtable.h中定义如下:

     extern pgd_t swapper_pg_dir[1024];

     extern unsigned long pg0[1024];

     swapper_pg_dir为页目录表,pg0为一临时页表,每个表最多都有1024项。

 2.线性地址域的定义

     Intel线性地址的结构如图2.29所示:


        31                22 21        11              0

                   2.29  32位的线性地址结构

(1) 偏移量的位数

  #define PAGE_SHIFT      12

  #define PAGE_SIZE       (1UL << PAGE_SHIFT)

      #define PTRS_PER_PTE    1024

  #define PAGE_MASK       (~(PAGE_SIZE-1))

   其中PAGE_SHIFT宏定义了偏移量的位数为12,因此页大小PAGE_SIZE2124096字节; PTRS_PER_PTE为页表的项数;最后PAGE_MASK值定义为0xfffff000,用以屏蔽掉偏移量域的所有位(12位)。

(2) PGDIR_SHIFT

   #define PGDIR_SHIFT     22

   #define PTRS_PER_PGD    1024

   #define PGDIR_SIZE      (1UL << PGDIR_SHIFT)

   #define PGDIR_MASK      (~(PGDIR_SIZE-1))

   PGDIR_SHIFT是页表所能映射区域线性地址的位数,它的值为2212位的偏移量加上10位的页表);PTRS_PER_PGD为页目录目录项数;PGDIR_SIZE为页目录的大小,222,即4MBPGDIR_MASK0xffc00000,用于屏蔽偏移量位与页表域的所有位。

3PMD_SHIFT

#define PMD_SHIFT       22

#define PTRS_PER_PMD    1

   PMD_SHIFT为中间目录表映射的地址位数,其值也为22,但是因为Linux386中只用了两级页表结构,因此,让其目录项个数为1,这就使得中间目录在指针序列中的位置被保存,以便同样的代码在32位系统和64位系统下都能使用。后面的讨论我们不再提及中间目录。