0%

《趣谈linux操作系统》小结(二十一) -内存映射

内核态内存映射

内核页表

在系统初始化的时候,我们就要创建内核页表了。

内核页表的根 swapper_pg_dir, 在 arch/x86/include/asm/pgtable_64.h 中就能找到它的定义。swapper_pg_dir 指向内核最顶级的目录 pgd,同时出现的还有几个页表目录。其中 XXX_ident_pgt 对应的是直接映射区,XXX_kernel_pgt 对应的是内核代码区,XXX_fixmap_pgt 对应的是固定映射区。

在汇编语言的文件里面的 arch\x86\kernel\head_64.S初始化这些页表的值,具体的汇编语言看不懂,呵呵。知道他们定义在__INITDATA 里面就可以了。页表的根其实是全局变量,这就使得我们初始化的时候,甚至内存管理还没有初始化的时候,很容易就可以定位到。最后定义成如下 一个树形的结构:

图片替换文本

内核页表定义完了,一开始这里面的页表能够覆盖的内存范围比较小。例如,内核代码区 512M,直接映射区 1G。这个时候,其实只要能够映射基本的内核代码和数据结构就可以了。可以看出,里面还空着很多项,可以用于将来映射巨大的内核虚拟地址空间,等用到的时候再进行映射。

用户态进程页表,会有 mm_struct 指向进程顶级目录 pgd,对于内核来讲,也定义了一个 mm_struct,指向 swapper_pg_dir,名为init_mm。

在初始化的时候会调用setup_arch初始化init_mm的成员,创建虚拟地址和物理地址的映射页表。

vmalloc 和 kmap_atomic 原理

在虚拟地址空间里面,有个 vmalloc 区域,从 VMALLOC_START 开始到 VMALLOC_END,可以用于映射一段物理内存。

从vmalloc的函数注释可以看出, 这个函数会从页分配器中分配足够大小的页,然后映射到vmalloc区域

1
2
3
4
5
6
7
8
9
10
11
12
/** 
* vmalloc - allocate virtually contiguous memory
* @size: allocation size
* Allocate enough pages to cover @size from the page level
* allocator and map them into contiguous kernel virtual space.
*
* For tight control over page level allocator and protection flags * use __vmalloc() instead.
*/

void *vmalloc(unsigned long size) {
...
}

内核的临时映射函数 kmap_atomic

如果是 32 位有高端地址的,就需要调用 set_pte 通过内核页表进行临时映射;如果是 64 位没有高端地址的,就调用 page_address,里面会调用 lowmem_page_address。其实低端内存的映射,会直接使用 __va 进行临时映射。

内核态缺页异常

kmap_atomic 发现,没有页表的时候,就直接创建页表进行映射了。而 vmalloc 没有,它只分配了内核的虚拟地址。所以,访问它的时候,会产生缺页异常。内核态的缺页异常还是会调用 do_page_fault,然后调用vmalloc_fault, 关联内核页表项。

图片替换文本

行动,才不会被动!

欢迎关注个人公众号 微信 -> 搜索 -> fishmwei,沟通交流。