Linux 内存管理 II

本文主要讨论Linux对物理地址空间的分页机制的实现。

分页机制说来也简单,借助MMU来实现进程虚拟地址空间到物理地址空间的映射,在x86平台中,PGD的基地址存放在CR3寄存器,对于ARM平台,在TIBRx寄存器,每当切换进程上下文的时候,Linux都会将本进程的PGD基地址写入相应的寄存器,如此便更换了虚拟地址到物理地址的映射关系,其产生的结果的就是不同进程中的同一个虚拟地址,经过PageGlobleDentry->PageUpperDentry->PageMiddleDentry->PageTableEntry的映射,找到的是不同的物理页框pfn,也就找到了不同的物理地址。

上图是比较典型的32bit CPU中3级页表模型,在64bit视物理地址总线宽度的不同(eg ARM64采用48bit寻址)可能会采用更多级的页表,但其基本原来都是一样的。

MMU除了要完成相应地址的映射,还要进行权限检查,由于地址映射都是以页为单位的,所以权限检查也是以页为单位的。具体地,每种CPU的MMU支持的权限标志位可能都不一样,以ARM Linux为例,其使用在权限定义在arch/arm/include/asm/pgtable*.h中,包括标记页面是否在主存中的L_PTE_PRESENT、标记页面是否是脏页的L_PTE_DIRTY、以及页面是否可执行的PAGE_SHARED_EXEC等。当Linux触发Page Fault时,就会准备好平台相关的MMU表项结构:映射关系和权限,放在相应的页目录中以供MMU使用。

PHYS_OFFSET,PAGE_OFFSET

这两个宏是系统对于物理地址空间划分很常用的两个宏,由于内核的虚拟地址空间和物理地址空间存在线性映射的关系,通过这两个宏,就可以很快地完成转换

Macro含义备注
PHYS_OFFSETRAM偏移物理地址空间RAM的起始地址
PAGE_OFFSETkernel space偏移虚拟地址空间中内核地址空间的起始地址

pgd_t, pud_t, pmd_t

这几个变量的类型分别是PGD,PUD,PMD的基地址,主要注意的是,作为物理地址,它们的本质是unsigned long, 而不是指针。

*_SHIFT, *_MASK

这两类宏在内存管理也很常见,前者是用于计算相应空间的大小的对数,比如,对于页来说

#define PAGE_SHIFT 12
#define PAGE_SIZE (1<<PAGE_SHIFT)

类似的还有PMD_SHIFT,PUD_SHIFT,PGDIR_SHIFT等。

后者用于计算屏蔽低位的掩码。

#define PAGE_MASK   (~(PAGE_SIZE-1))

ALIGN()

要解释这个宏,就需要看看x&~(2^n -1 )的计算结果。对于一个2的指数,其二进制假设为0x10000,则0x10000 – 1 = 0x01111, ~0x01111 = 0x1111111…0000,可见任何一个数x与该数做”与”运算,低位都会被掩掉,也就是向下对齐的效果,即

#define ALIGN(x,a) (x&~(a-1))

同理,对于向上对齐,我们只需要的使用(x+(a-1))&~(a-1)即可,这就是ALIGN。

ALLIGN(x,a)用于x以a为标准向上对齐的值,a必须是2的次方,定义如下:

#define ALIGN(x,a)              __ALIGN_MASK(x,(typeof(x))(a)-1)
#define __ALIGN_MASK(x,mask)    (((x)+(mask))&~(mask))

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.