linux kernel 学习笔记二 内存,趣谈Linux操作系统学习笔记-内存管理(26讲)-- 内核的映射机制...

问题:

1. 内核态内存映射函数 vmalloc、kmap_atomic 是如何工作的;

2. 内核态页表是放在哪里的,如何工作的?

3. swapper_pg_dir 是怎么回事;

4. 出现了内核态缺页异常应该怎么办?

内核页表

注意:和用户态页表不同,在系统初始化的时候,我们就要创建内核页表了

从内核页表的根 swapper_pg_dir 开始找线索,在 arch/x86/include/asm/pgtable_64.h 中就能找到它的定义

1 extern pud_t level3_kernel_pgt[512];2 extern pud_t level3_ident_pgt[512];3 extern pmd_t level2_kernel_pgt[512];4 extern pmd_t level2_fixmap_pgt[512];5 extern pmd_t level2_ident_pgt[512];6 extern pte_t level1_fixmap_pgt[512];7 externpgd_t init_top_pgt[];8

9

10 #define swapper_pg_dir init_top_pgt

swapper_pg_dir 指向内核最顶级的目录 pgd,同时出现的还有几个页表目录。

64 位系统的虚拟地址空间的布局:

其中 XXX_ident_pgt 对应的是直接映射区

XXX_kernel_pgt       对应的是内核代码区

XXX_fixmap_pgt      对应的是固定映射区

e0225efaeea6079dd9975bd549011fc5.png

内核页表的顶级目录 init_top_pgt,

init_top_pgt 有三项:

第一项:

指向的是 level3_ident_pgt,也即直接映射区页表的三级目录

为什么要减去 __START_KERNEL_map 呢?

因为 level3_ident_pgt 是定义在内核代码里的,写代码的时候,写的都是虚拟地址,谁写代码的时候也不知道将来加载的物理地址是多少

因为 level3_ident_pgt 是在虚拟地址的内核代码段里的,而 __START_KERNEL_map 正是虚拟地址空间的内核代码段的起始地址,level3_ident_pgt 减去 __START_KERNEL_map 才是物理地址

第二项:PGD_PAGE_OFFSET

__PAGE_OFFSET_BASE 是虚拟地址空间里面内核的起始地址。第二项也指向 level3_ident_pgt,直接映射区

第三项:PGD_START_KERNEL

__START_KERNEL_map 是虚拟地址空间里面内核代码段的起始地址。第三项指向 level3_kernel_pgt,内核代码区

vmalloc 和 kmap_atomic 原理

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

1 /**2 * vmalloc - allocate virtually contiguous memory3 * @size: allocation size4 * Allocate enough pages to cover @size from the page level5 * allocator and map them into contiguous kernel virtual space.6 *7 * For tight control over page level allocator and protection flags8 * use __vmalloc() instead.9 */

10 void *vmalloc(unsigned longsize)11 {12 return__vmalloc_node_flags(size, NUMA_NO_NODE,13 GFP_KERNEL);14 }15

16

17 static void *__vmalloc_node(unsigned long size, unsigned longalign,18 gfp_t gfp_mask, pgprot_t prot,19 int node, const void *caller)20 {21 return__vmalloc_node_range(size, align, VMALLOC_START, VMALLOC_END,22 gfp_mask, prot, 0, node, caller);23 }

kmap_atomic 的实现:

1 void *kmap_atomic_prot(struct page *page, pgprot_t prot)2 {3 ......4 if (!PageHighMem(page))5 returnpage_address(page);6 ......7 vaddr = __fix_to_virt(FIX_KMAP_BEGIN +idx);8 set_pte(kmap_pte-idx, mk_pte(page, prot));9 ......10 return (void *)vaddr;11 }12

13

14 void *kmap_atomic(struct page *page)15 {16 returnkmap_atomic_prot(page, kmap_prot);17 }18

19

20 static __always_inline void *lowmem_page_address(const struct page *page)21 {22 returnpage_to_virt(page);23 }24

25

26 #define page_to_virt(x) __va(PFN_PHYS(page_to_pfn(x)

1. 如果是 32 位有高端地址的,就需要调用 set_pte 通过内核页表进行临时映射;

2. 如果是 64 位没有高端地址的,就调用 page_address,里面会调用 lowmem_page_address。

3. 其实低端内存的映射,会直接使用 __va 进行临时映射。

内核态缺页异常

kmap_atomic 发现,没有页表的时候,就直接创建页表进行映射了。而 vmalloc 没有,它只分配了内核的虚拟地址。所以,访问它的时候,会产生缺页异常

内核态的缺页异常还是会调用 do_page_fault,这个函数前面分析了已经,

说一下vmalloc_fault:

1 /*

2 * 32-bit:3 *4 * Handle a fault on the vmalloc or module mapping area5 */

6 static noinline int vmalloc_fault(unsigned longaddress)7 {8 unsigned longpgd_paddr;9 pmd_t *pmd_k;10 pte_t *pte_k;11

12

13 /*Make sure we are in vmalloc area:*/

14 if (!(address >= VMALLOC_START && address

17

18 /*

19 * Synchronize this task's top level page-table20 * with the 'reference' page table.21 *22 * Do _not_ use "current" here. We might be inside23 * an interrupt in the middle of a task switch..24 */

25 pgd_paddr =read_cr3_pa();26 pmd_k =vmalloc_sync_one(__va(pgd_paddr), address);27 if (!pmd_k)28 return -1;29

30

31 pte_k =pte_offset_kernel(pmd_k, address);32 if (!pte_present(*pte_k))33 return -1;34

35

36 return 0

总结:

1、物理内存管理

0e3573c5c580a7a3878b0e6ec0aaa358.png

物理内存根据 NUMA 架构分节点。每个节点里面再分区域。每个区域里面再分页。

物理页面通过伙伴系统进行分配。分配的物理页面要变成虚拟地址让上层可以访问,kswapd 可以根据物理页面的使用情况对页面进行换入换出。

2、内存分配内核态

ed604a0cc619625ae58bc2af4bf4510d.png

对于内核态,kmalloc 在分配大内存的时候,以及 vmalloc 分配不连续物理页的时候,直接使用伙伴系统,分配后转换为虚拟地址,访问的时候需要通过内核页表进行映射

对于 kmem_cache 以及 kmalloc 分配小内存,则使用 slub 分配器,将伙伴系统分配出来的大块内存切成一小块一小块进行分配。

kemem_cache和kmalloc的部分不会被换出、因为用这两个函数分配的内存多用于保存内核关键的数据结构,内核中vmalloc分配的部分会被换出,因而当访问的时候、发现不再

就会调用do_page_fault

3、内存分配用户态

9052f5552bcb99b1753ad751db6f2c48.png

对于用户态的内存分配,或者直接调用 mmap 系统调用分配,或者调用 malloc。调用 malloc 的时候,如果分配小的内存,就用 sys_brk 系统调用;如果分配大的内存,还是用 sys_mmap 系统调用。

正常情况下,用户态的内存都是可以换出的,因而一旦发现内存中不存在,就会调用 do_page_fault

4、内存分配体系总图

64dd5e651955ecb0398e7f347331733c.png

PGD_START_KERNEL

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值