上篇文章介绍了如何查看内核使用的分页模式,笔者电脑上内核使用的分页模式是 4-level paging,这篇文章讲解 4-level paging分页模式如何将线性地址转换成物理地址.
先思考2个问题:
- 内核初始化时,线性地址0xffffffff8220a000转换成物理地址是多少?
- 内核初始化时,线性地址0xffff88800220a000转换成物理地址是多少?
1. 4-level paging 转换过程
先看一张intel手册上的4-level paging 4KB大小的页的转换图
下面介绍这张图表达的内容:
2. cr3寄存器介绍
CR3寄存器又叫页目录基址寄存器(Page Directory Base Register, PDGR), CR3中存放着当前任务页表目录的物理地址.
3. 内核中线性地址和物理地址转换宏
下面代码使用内核中定义的宏打印物理地址,代码中的宏选自linux5.4.34 arch/x86/include/asm/page.h
arch/x86/include/asm/page_64.h
#include <stdio.h>
#define __AC(X,Y) (X##Y)
#define _AC(X,Y) __AC(X,Y)
#define __PAGE_OFFSET_BASE_L4 _AC(0xffff888000000000, UL)
#define __PAGE_OFFSET __PAGE_OFFSET_BASE_L4
#define PAGE_OFFSET ((unsigned long)__PAGE_OFFSET)
#define __START_KERNEL_map _AC(0xffffffff80000000, UL)
// __va宏是将物理地址转换成线性地址,直接等于物理地址 + 0xffff888000000000
#define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET))
static inline unsigned long __phys_addr_nodebug(unsigned long x)
{
unsigned long y = x - __START_KERNEL_map;
/* use the carry flag to determine if x was < __START_KERNEL_map */
// 笔者电脑上phys_base为0
x = y + ((x > y) ? 0 /* phys_base */ : (__START_KERNEL_map - PAGE_OFFSET));
return x;
}
#define __phys_addr(x) __phys_addr_nodebug(x)
#define __phys_addr_symbol(x) \
((unsigned long)(x) - __START_KERNEL_map + 0 /* phys_base */) // phys_base为0
#define __phys_reloc_hide(x) (x)
// __pa宏是将线性地址转换成物理地址
#define __pa