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_CORE
if (crashk_res.end)
memblock_mark_nomap(crashk_res.start,
resource_size(&crashk_res));
#endif
/* map all the memory banks */
#開始映射memblock中的memory
for_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_NOMAP
memblock_clear_nomap(kernel_start, kernel_end - kernel_start);
}