《深入Linux内核架构》第3章 内存管理(3)

目录

3.3 页表

3.3.1 数据结构

3.3.2 特定于PTE的信息

3.3.3 页表项的操作


本专栏文章将有70篇左右,欢迎+关注,订阅后续文章。

3.3 页表

页表作用:

        包含将虚拟地址转换为物理地址的信息。

内核总是使用四级页表。所以如果一个体系架构只支持两级页表,则还需模拟第三和第四级页表。

四级页表:

        PGD(Page Global Directory):页全局目录

        PUD(Page Upper Directory):页上层目录

        PMD(Page Middle Directory):页中间目录

        PTE(Page Table Entry) :页表项

如何将一个虚拟地址找到对应物理地址?(即页表查询过程)

        1. 读取页表基址寄存器 (PTBR) 值,其中存放PGD页表的物理地址。

        2. 根据虚拟地址算出PGD index,再结合PGD物理地址,得到PUD物理地址。

        3. 根据虚拟地址算出PUD index,再结合PUD物理地址,得到PMD物理地址。

        4. 根据虚拟地址算出PMD index,再结合PMD物理地址,得到PTE物理地址。

  

不同的体系架构中用不同寄存器作为基址寄存器 (PTBR)

        x86:CR3(Control Register 3)

        ARM:TTBR(Translation Table Base Register)

3.3.1 数据结构

一个虚拟地址使用unsigned long定义。

对于所有体系架构都成立:

        sizeof(void *) = sizeof(unsigned long)

一个虚拟地址分为5部分:

#define PAGE_SHIFT         12

        则一个页大小2的12次方,即4K。

#define PAGE_SIZE        (1UL << PAGE_SHIFT)

        即一个页大小。


      

PTRS_PER_PGD:页全局目录中表项数

PTRS_PER_PUD:页上层目录中表项数

PTRS_PER_PMD:页中间目录中表项数

PTRS_PER_PTE:页表项中表项数

对于只支持两级页表的体系:

        PTRS_PER_PMD=1

        PTRS_PER_PUD=1

一个页表项的数据结构,pgd和pte为例:

        typedef struct {

                unsigned long         pgd;

        } pgd_t;

        typedef struct {

                unsigned long         pte;

        } pte_t;

将 pte_t 转换为 unsigned long:

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

        #define pte_val(x)         ((x).pte)

将 unsigned long 转换为 pte_t:

        #define __pgd(x)         ((pgd_t) { (x) } )

        #define __pte(x)         ((pte_t) { (x) } )

计算一个给定虚拟地址的PMD index:

        pmd_index(unsigned long addr)

计算一个给定地址虚拟地址在页上层目录PUD指向的页中间目录(PMD)的索引值

        pmd = pmd_offset(pud_t *pud, unsigned long addr)

将一个给定地址返回按页对齐后地址:

        addr = PAGE_ALIGN(addr);

        如PAGE_ALIGN(6000) ,则返回8192。

3.3.2 特定于PTE的信息

一个PTE页表项条目:

        除了指示物理内存页的位置,还利用多余bit实现如下信息。

ARM为例:

#define        L_PTE_VALID                (_AT(pteval_t, 1)

        该页表条目是有效的

#define        L_PTE_PRESENT        (_AT(pteval_t, 1)

        虚拟页已加载到物理内存中

#define        L_PTE_FILE                (_AT(pteval_t, 1)

        该页表项对应的物理页为磁盘文件映射页

#define        L_PTE_DIRTY                (_AT(pteval_t, 1)

        该页表项指向的物理页有脏数据,待写回磁盘

#define        L_PTE_RDONLY                (_AT(pteval_t, 1)

        该页表项指向的物理页是只读的,如程序的代码段页面。

#define        L_PTE_USER                (_AT(pteval_t, 1)

        该页面只可被用户进程访问。不能被内核访问。

#define         L_PTE_XN                (_AT(pteval_t, 1)

        即eXecute Never,该页面不允许执行指令。

                如堆栈,数据段页面。

#define        L_PTE_SHARED        (_AT(pteval_t, 1)

        该页表项指向的物理页可被多个进程共享

#define        L_PTE_NONE        (_AT(pteval_t, 1)

        该页表项无效或不存在

注意:之前说过struct page表示一个物理页,其内部 flag 成员表示的是物理页的状态信息。

flag成员与PTE项的状态对比:

        二者区别:

                PTE关注虚拟内存层面的标志信息。给MMU和虚拟内存管理系统使用的。

                而struct page中flags关注物理内存层面。给内存管理子系统使用。

        二者关联:

                二者标志信息可能需要互相同步,赋值。

MMU进行虚拟地址转换物理地址时,将读取PTE中标志信息。

ARM中实现有:

        pte_t        pte_modify(pte_t pte, pgprot_t newprot)

                修改pte页表项对应bit,完成对应标志更改。

        #define        pte_present(pte)        (pte_val(pte) & L_PTE_PRESENT)

                检查一个页表项指向的物理页是否存在且有效

        #define        pte_dirty(pte)        (pte_val(pte) & L_PTE_DIRTY)

                检查一个页表项指向的物理页是否有脏数据

        #define        pte_write(pte)        (!(pte_val(pte) & L_PTE_RDONLY))

                检查该页表项指向的物理页是否可写

应用层malloc时,内核如何分配内存?

        1. 内核在虚拟地址空间分配一块虚拟内存。

        2. 创建页表项PTE,标记页表项为空或者无效,并设置页表项为保护状态。

        3. 当应用程序首次写虚拟内存时,因为页表项为保护状态,所以会触发page fault。

        4. page fault中断处理中,通过页置换或页换出等算法找到可用物理页,并将该物理页映射到引起错误的虚拟地址。

        5. 最后更新页表项,包括设置PTE_PRESENT,也可能设置PTE_DIRTY(页已写入)。

3.3.3 页表项的操作

pte_t  pte = mk_pte(struct page *page, pgprot_t pgprot)

        定义一个页表项PTE,用于指向page物理页,并设置相应属性pgprot。

pud_t  *pud_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address)

        在PGD中分配一个PUD条目。

pmd_t  *pmd_alloc(struct mm_struct *mm, pud_t *pud, unsigned long address)

        在PUD中分配一个PMD条目。

int  __pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma,

pmd_t *pmd, unsigned long address)

        在给定虚拟内存区域(VMA)的虚拟地址address分配一个新的页表项(PTE)。

        常用于创建新的VMA或扩展现有VMA。

void pte_free(struct mm_struct *mm, pgtable_t pte)

        释放页表条目PTE。

  • 29
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山下小童

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值