物理内存映射
kernel 通過paging_init來映射物理內存
void __init paging_init(void)
{#為pgd申請內存并賦值,這時候還沒有buddy system,因此在早期都是通過memblock來申請內存的phys_addr_t pgd_phys = early_pgtable_alloc();pgd_t *pgdp = pgd_set_fixmap(pgd_phys);#可以看到kernel本身占用的內存是單獨映射的,這兩個函數我們后面詳細分析map_kernel(pgdp);map_mem(pgdp);#在head.s中有申請一段memory來臨時映射物理內存swapper_pg_dir,這里我們可以復用#這段內存,這樣我們就可以把通過memblock申請的pgd_phys 釋放掉cpu_replace_ttbr1(__va(pgd_phys));memcpy(swapper_pg_dir, pgdp, PGD_SIZE);cpu_replace_ttbr1(lm_alias(swapper_pg_dir));pgd_clear_fixmap();#釋放pgd_phys 占用的內存memblock_free(pgd_phys, PAGE_SIZE);/** We only reuse the PGD from the swapper_pg_dir, not the pud + pmd* allocated with it.*/#我們只是復用swapper_pg_dir 中pgd的memory,因此治理釋放到pud和pmd 占用的內存memblock_free(__pa_symbol(swapper_pg_dir) + PAGE_SIZE,__pa_symbol(swapper_pg_end) - __pa_symbol(swapper_pg_dir)- PAGE_SIZE);
}
首先看看map_kernel
static void __init map_kernel(pgd_t *pgdp)
{static struct vm_struct vmlinux_text, vmlinux_rodata, vmlinux_inittext,vmlinux_initdata, vmlinux_data;pgprot_t text_prot = rodata_enabled ? PAGE_KERNEL_ROX : PAGE_KERNEL_EXEC;#從下面這段可以看出kernel 本身占用的memory可以分為下面這5段map_kernel_segment(pgdp, _text, _etext, text_prot, &vmlinux_text, 0,VM_NO_GUARD);map_kernel_segment(pgdp, __start_rodata, __inittext_begin, PAGE_KERNEL,&vmlinux_rodata, NO_CONT_MAPPINGS, VM_NO_GUARD);map_kernel_segment(pgdp, __inittext_begin, __inittext_end, text_prot,&vmlinux_inittext, 0, VM_NO_GUARD);map_kernel_segment(pgdp, __initdata_begin, __initdata_end, PAGE_KERNEL,&vmlinux_initdata, 0, VM_NO_GUARD);map_kernel_segment(pgdp, _data, _end, PAGE_KERNEL, &vmlinux_data, 0, 0);#最終map_kernel_segment->__create_pgd_mapping->pud->pmd->pte等來映射
}
memblock 中的memory映射如下:
static void __init map_mem(pgd_t *pgdp)
{phys_addr_t kernel_start = __pa_symbol(_text);phys_addr_t kernel_end = __pa_symbol(__init_begin);struct memblock_region *reg;int flags = 0;#是否開debug選項if (debug_pagealloc_enabled())flags = NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS;/** Take care not to create a writable alias for the* read-only text and rodata sections of the kernel image.* So temporarily mark them as NOMAP to skip mappings in* the following for-loop*/#由于kernel 占用的memory頁包含在memblock中,且我們再前面已經映射kernel了,所以#這里標記kernel占用的memory不用在重復映射了memblock_mark_nomap(kernel_start, kernel_end - kernel_start);
#ifdef CONFIG_KEXEC_COREif (crashk_res.end)memblock_mark_nomap(crashk_res.start,resource_size(&crashk_res));
#endif/* map all the memory banks */#開始映射memblock中的memoryfor_each_memblock(memory, reg) {phys_addr_t start = reg->base;phys_addr_t end = start + reg->size;#起始地址大于結束地址的話,肯定更有問題,退出if (start >= end)break;#如果memblock包含MEMBLOCK_NOMAP,則不用映射,例如前面提到的kernel的映射if (memblock_is_nomap(reg))continue;#開始映射memblock中的memory__map_memblock(pgdp, start, end, PAGE_KERNEL, flags);}__map_memblock(pgdp, kernel_start, kernel_end,PAGE_KERNEL, NO_CONT_MAPPINGS);#清除kernel memory段的MEMBLOCK_NOMAPmemblock_clear_nomap(kernel_start, kernel_end - kernel_start);}
總結
- 上一篇: 快手影音 www.kuaishou.ne
- 下一篇: 快手 (kuaishou.net) 2.