linux内存初始化管理,Linux内存管理以内核分页机制(paging_init)初始化

背景

前面分析了kernel boot阶段内存管理实现的分段机制,能够发现页表描述符是按照ARM MMU硬件页表转换逻辑来设计的。kernel在初始化过程当中只映射了内核 image部分的物理内存,在某个合适的时候内核须要将尽量多的物理内存映射到页表中。

Linux设计为通用的操做系统,为了便于移植须要抽象出一些硬件细节,咱们在驱动代码中看到大量的core层代码就是这个思想的体现。内核中只有和硬件彻底相关的代码才会单独实现,这样作便于移植和添加新硬件。在内存管理中涉及到的PGD,PMD,PUD,PTE等要结合上下文理解是属于硬件仍是软件层上的概念。对于32bit的ARM MMU根据相关文档最多只支持到二级页表。因此内核没有用到PMD和PUD。

pud_offsetlinux

38 static inline pud_t * pud_offset(pgd_t * pgd, unsigned long address)

39 {

40 return (pud_t *)pgd;

41 }

pmd_offsetweb

160 static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr)

161 {

162 return (pmd_t *)pud;

163 }

能够在ARM平台上调用pmd和pud相关结构去获取offset其实直接返回的是pgd的offset。数组

变量

KERNEL_RAM_VADDR

PAGE_OFFSET + TEXT_OFFSET = 0x8000 0000 + 0x0000 8000,

表示内核image在RAM中的起始地址

PG_DIR_SIZE

0x0000 4000

swapper_pg_dir

KERNEL_RAM_VADDR - PG_DIR_SIZE = 0x8000 8000 - 0x0000 4000

表示内核页表的起始地址

pgd的基址

PGDIR_SHIFT

21

-MODULES_VADDR

PAGE_OFFSET - SZ_16M = 0x8000 0000 - 0x0100 0000 = 0x7F00 0000

众所周知内核会把地址空间划分红user space和kernel space。通常都说user space位于低地址的0~3G空间,kernel位于0xC000 0000开始的1G空间。可是tiny4412平台kernel space是从0x8000 0000开始的,也就是把0~4G对半分了,user space占低地址的2GB,kernel占高地址的2GB。

但仔细研究能够发现起始user space并无用满2GB。好比说这里的MODULES_VADDR就是位于kernel space和user space之间,而且占用的是user space尾部的16M。

pgd_t

这竟然是一个数组!若是不懂pgd_t为何要定义成pgd[2]这样的二元数组,那必定不懂内核分页机制的实现过程!至少不算彻底懂!

pgd定义成数组的缘由在源码arch/arm/include/asm/pgtable-2level.h的注释中已经说明,能够说注释中的图包含了内核页表转换的精髓!

35 * pgd pte

36 * | |

37 * +--------+

38 * | | +------------+ +0

39 * +- - - - + | Linux pt 0 |

40 * | | +------------+ +1024

41 * +--------+ +0 | Linux pt 1 |

42 * | |-----> +------------+ +2048

43 * +- - - - + +4 | h/w pt 0 |

44 * | |-----> +------------+ +3072

45 * +--------+ +8 | h/w pt 1 |

46 * | | +------------+ +4096

从解释中能够发现linux约定一次分配1page 4KB的大小来给硬件pte使用。根据ARM硬件小页地址变换过程可知。一条一级页表描述符(*pgd)能表示2的8 次方(256)个二级页表条目(pte),每条pte占4Byte,总共硬件pte须要占用256*4Byte=1KB。那咱们分1个page全给硬件pte使用吗?并非!linux遵循软硬件分离的设计思想,linux自己包含软件pte,size和硬件pte同样,只是pte描述符中控制位表示的含义不一样。既然咱们每描述一个硬件pte都须要对应有一个软件的pte,那索性linux就将一个page对半分,一半给硬件pte,通常给对应的软件pte。这样一来,半个page能够存放2*256条pte,也就是两个pgd元素,因此这里定义了pgd[2]这个数组!内核约定一个page中的上半部分给软件pte使用,下半部分给硬件pte使用。这样彻底用完一个page一点不浪费。数据结构

PGDIR_SIZE

1<< PGDIR_SHIFT == 1 << 21 == 2MB

为何是21bit而不是20bit?由于根据上一个变量的说明可知,内核为了完整使用一个page的空间须要在一个page中存放两条pgd对应的pte条目,一个pgd能够映射1MB物理内存,2个pgd就能映射2MB物理内存。因此内核应该是一次性映射2MB的物理内存。

*init_mm

内核定义了一个静态mm_struct数据结构来管理内核页表相关定义。看下这里的pgd成员,swapper_pg_dir

16 struct mm_struct init_mm = {

17 .mm_rb = RB_ROOT,

18 .pgd = swapper_pg_dir,

19 .mm_users = ATOMIC_INIT(2),

20 .mm_count = ATOMIC_INIT(1),

21 .mmap_sem = __RWSEM_INITIALIZER(init_mm.mmap_sem),

22 .page_table_lock = __SPIN_LOCK_UNLOCKED(init_mm.page_table_lock),

23 .mmlist = LIST_HEAD_INIT(init_mm.mmlist),

24 INIT_MM_CONTEXT(init_mm)

25 };

-*VMALLOC_START

high_mem + VMALLOC_OFFSET

- VMALLOC_END

0xFF00 0000

- VMALLOC_OFFSET

8*1024*1024

- high_mem

high_mem = arm_lowmem_limit

- vmalloc_limit && vmalloc_min

vmalloc_limit == vmalloc_min

vmalloc_min = (VMALLOC_END - (240 << 20) - VMALLOC_OFFSET) = 0xFF00 0000 - 240MB - 8MB

物理地址越大vmalloc的空间越小,可是最小不得小于240M B+8MB

- arm_lowmem_limit

内核会区分high mem和low mem,二者使用方法不一样。这里的arm_lowmem_limit就是用来记载当前物理内存low_mem段的大小,high mem则用于vmalloc。vmalloc有最小虚拟空间的限制,最小不能小于这个值。这个值就是vmalloc_limit,vmalloc_limit表示vmalloc的起始地址。当实际物理内存大于vmalloc_limit时,内核会将low_mem限制到vmalloc_limit;若是实际物理内存小于vmalloc_limit则将全部的物理内存赋给low_mem,vmalloc就会变大,变成从物理内存结束到0xFF00 0000为止的全部虚拟地址给vmalloc使用,远大于(240 + 8)MB。app

virtual memory map

0af8bb0b6f3d09b6e22cf58847b9d633.png

paging_init流程分析

有了前面的基础,paging_init的流程比较好理解了。初始化memory 类型,清理boot阶段的页表这些就不看了,直接看建立页表的关键函数map_lowmem()。svg

1515 void __init paging_init(const struct machine_desc *mdesc)

1516 {

1517 void *zero_page;

1518

1519 build_mem_type_table();

1520 prepare_page_table();

1521 map_lowmem();

1522 dma_contiguous_remap();

1523 devicemaps_init(mdesc);

1524 kmap_init();

1525 tcm_init();

1526

1527 top_pmd = pmd_off_k(0xffff0000);

1528

1529 /* allocate the zero page. */

1530 zero_page = early_alloc(PAGE_SIZE);

1531

1532 bootmem_init();

1533

1534 empty_zero_page = virt_to_page(zero_page);

1535 __flush_dcache_page(NULL, empty_zero_page);

1536 }

从map_lowmem的函数名能够看出,这个函数会将lowmem部分的一级pgd页表填充初始化。1MB对齐部分的物理内存会被初始化pgd中,不足1MB的会再经过pte来映射。

到此,boot阶段初始化的页表就被覆盖掉了。且可能同时存在分段和分页两种形式的映射函数

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值