paging_init 用来建立页表,初始化zone的memory map
void *zero_page;
sort(&meminfo.bank, meminfo.nr_banks, sizeof(meminfo.bank[0]), meminfo_cmp, NULL);
build_mem_type_table();
sanity_check_meminfo();
prepare_page_table();
map_lowmem();
bootmem_init();
devicemaps_init(mdesc);
kmap_init();
top_pmd = pmd_off_k(0xffff0000);
/*
* allocate the zero page. Note that this always succeeds and
* returns a zeroed result.
*/
zero_page = alloc_bootmem_low_pages(PAGE_SIZE);
empty_zero_page = virt_to_page(zero_page);
__flush_dcache_page(NULL, empty_zero_page);
build_mem_type_table 这个函数根据CPU类型,设置mem_types全局数组,mem_types数组保存了页目录和页表的属性,将来创建页目录和页表时,会用到mem_types。
sanity_check_meminfo 检测判断是否需要创建highmem区,并且重建描述内存bank的数组
VMALLOC_MIN定义了vmalloc区的起始位置(通过VMALLOC_END和vmalloc_reserve计算得出),VMALLOC_END定义了vmalloc区的结束位置,vmalloc_reserve是系统预留给vmalloc区的大小。
prepare_page_table 这个函数会请空页目录,有两块地址空间区域是不需要清除的,一个是kernel image,另外一个是kernel线性地址映射区
map_lowmem 建立低端内存的所有页目录和页表:遍历memory bank,映射那些没有highmem标记的内存bank
bootmem_init :
1. 调用check_initrd获取initrd所在的memory bank对应的node
2. 对每一个节点:
- 获取该node的 min(最小pfn), node_low(最大low memory pfn), node_hight(最大high memory pfn)
- 调用bootmem_init_node初始化node,bootmem_init_node会初始化bootmem bitmap
- 如果是node 0,那么调用reserve_node_zero为node 0 reserve的内存:内核text和data区,初始化页表区(16KB),以及swapper_pg_dir之前的一块内存(在我的机器上是4个page)
- 如果initrd存放在当前的node上,那么调用bootmem_reserve_initrd保留initrd占用的内存。initrd_start是initrd在起始虚拟内存地址,initrd_end是initrd结束的虚拟内存地址。
3. 对每一个node 调用bootmem_free_node
- 设置这个node内各个zone的大小
- 调用free_area_init_node:计算node的总pages数目,为这个node分配mem map,注意node内所有zone的memmap都分配在一起
- 调用free_area_init_core:对于node内的每一个zone,进行初始化。注意这个函数present_pages是total size减去了该分区对应的memmap占用的pages,但是实际上memmap是放在node的开始位置,这里似乎不应该减去这个值
4. high_memory 是一个很奇怪的变量,high_memory应该是物理内存的概念,但是high_memory变量保存的确实一个内核地址。
devicemaps_init 这个函数创建device的映射,
1. 把machine vectors映射到0xffff0000处
2. 调用平台特定的map_io,对于mx51,这个函数主要是映射mx51功能寄存器区, AIPS1 AIPS2 和SPBA0,这三个寄存器区大小为1MB,映射后的虚拟地址分别为0xF7E00000,0xF7D00000,0xFB100000
kmap_init 创建pkmap的pgd和pte。并且让pkmap_page_table指向这个PTE page的linux p/t。一般来说kmap都是使用一个page的pte来映射高端内存到内核地址空间,对于arm来说,每个page可以存放512个pte_t,所以pkmap的地址空间为2M。
empty zero page
按照源码注释,它是一个特定的初始化为0的页,用于COW
我的理解是,系统有时需要一个页面全零,这种情况下,并不需要分配一个全零的页面,而是让PTE指向empty_zero_page,当试图写这个page时,由于这个empty_zero_page是共享的,所以导致了COW。