【Linux 内核 内存管理】Linux 内核内存布局 ④ ( ARM64 架构体系内存分布 | 内核启动源码 start_kernel | 内存初始化 mm_init | mem_init )
文章目錄
- 一、ARM64 架構體系內存分布
- 二、Linux 內核啟動源碼 start_kernel
- 三、內存初始化源碼 mm_init
- 四、內存初始化源碼 mem_init
一、ARM64 架構體系內存分布
ARM64 架構 的 " 物理地址 " 有 484848 位 , 理論上最大 " 尋址空間 " 為 256256256 TB ;
ARM64 架構 的 " 虛擬地址 " 也是 最大支持 484848 位 尋址地址 ;
Linux 內核 將 " 地址空間 " 劃分為 : 內核空間 和 用戶空間 ;
① 內核空間 ( Kernel Space ) : 尋址范圍 0x FFFF 0000 0000 0000 ~ 0x FFFF FFFF FFFF FFFF ;
② 用戶空間 ( User Space ) : 尋址范圍 0x 0000 0000 0000 0000 ~ 0x 0000 FFFF FFFF FFFF ;
如下圖所示 :
上圖中的 " 不規范地址空間 " 是不允許使用的 內存空間 ;
二、Linux 內核啟動源碼 start_kernel
在 Linux 內核初始化完成后 , 會在 " 初始化內存 " 時 , 輸出 內存布局 ;
Linux 內核啟動源碼是定義在 linux-5.6.18\init\main.c 源碼中的
asmlinkage __visible void __init start_kernel(void)函數 ;
在 Linux 內核啟動方法 中 , 調用了 mm_init(); 方法 , 參考路徑 : linux-5.6.18\init\main.c#878
asmlinkage __visible void __init start_kernel(void) {// .../** These use large bootmem allocations and must precede* kmem_cache_init()*/setup_log_buf(0);vfs_caches_init_early();sort_main_extable();trap_init();mm_init();// ... }Linux 內核 啟動源碼 ( 僅做參考 ) :
asmlinkage __visible void __init start_kernel(void) {char *command_line;char *after_dashes;set_task_stack_end_magic(&init_task);smp_setup_processor_id();debug_objects_early_init();cgroup_init_early();local_irq_disable();early_boot_irqs_disabled = true;/** Interrupts are still disabled. Do necessary setups, then* enable them.*/boot_cpu_init();page_address_init();pr_notice("%s", linux_banner);early_security_init();setup_arch(&command_line);setup_boot_config(command_line);setup_command_line(command_line);setup_nr_cpu_ids();setup_per_cpu_areas();smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */boot_cpu_hotplug_init();build_all_zonelists(NULL);page_alloc_init();pr_notice("Kernel command line: %s\n", saved_command_line);/* parameters may set static keys */jump_label_init();parse_early_param();after_dashes = parse_args("Booting kernel",static_command_line, __start___param,__stop___param - __start___param,-1, -1, NULL, &unknown_bootoption);if (!IS_ERR_OR_NULL(after_dashes))parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,NULL, set_init_arg);if (extra_init_args)parse_args("Setting extra init args", extra_init_args,NULL, 0, -1, -1, NULL, set_init_arg);/** These use large bootmem allocations and must precede* kmem_cache_init()*/setup_log_buf(0);vfs_caches_init_early();sort_main_extable();trap_init();mm_init();ftrace_init();/* trace_printk can be enabled here */early_trace_init();/** Set up the scheduler prior starting any interrupts (such as the* timer interrupt). Full topology setup happens at smp_init()* time - but meanwhile we still have a functioning scheduler.*/sched_init();/** Disable preemption - early bootup scheduling is extremely* fragile until we cpu_idle() for the first time.*/preempt_disable();if (WARN(!irqs_disabled(),"Interrupts were enabled *very* early, fixing it\n"))local_irq_disable();radix_tree_init();/** Set up housekeeping before setting up workqueues to allow the unbound* workqueue to take non-housekeeping into account.*/housekeeping_init();/** Allow workqueue creation and work item queueing/cancelling* early. Work item execution depends on kthreads and starts after* workqueue_init().*/workqueue_init_early();rcu_init();/* Trace events are available after this */trace_init();if (initcall_debug)initcall_debug_enable();context_tracking_init();/* init some links before init_ISA_irqs() */early_irq_init();init_IRQ();tick_init();rcu_init_nohz();init_timers();hrtimers_init();softirq_init();timekeeping_init();/** For best initial stack canary entropy, prepare it after:* - setup_arch() for any UEFI RNG entropy and boot cmdline access* - timekeeping_init() for ktime entropy used in rand_initialize()* - rand_initialize() to get any arch-specific entropy like RDRAND* - add_latent_entropy() to get any latent entropy* - adding command line entropy*/rand_initialize();add_latent_entropy();add_device_randomness(command_line, strlen(command_line));boot_init_stack_canary();time_init();perf_event_init();profile_init();call_function_init();WARN(!irqs_disabled(), "Interrupts were enabled early\n");early_boot_irqs_disabled = false;local_irq_enable();kmem_cache_init_late();/** HACK ALERT! This is early. We're enabling the console before* we've done PCI setups etc, and console_init() must be aware of* this. But we do want output early, in case something goes wrong.*/console_init();if (panic_later)panic("Too many boot %s vars at `%s'", panic_later,panic_param);lockdep_init();/** Need to run this when irqs are enabled, because it wants* to self-test [hard/soft]-irqs on/off lock inversion bugs* too:*/locking_selftest();/** This needs to be called before any devices perform DMA* operations that might use the SWIOTLB bounce buffers. It will* mark the bounce buffers as decrypted so that their usage will* not cause "plain-text" data to be decrypted when accessed.*/mem_encrypt_init();#ifdef CONFIG_BLK_DEV_INITRDif (initrd_start && !initrd_below_start_ok &&page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {pr_crit("initrd overwritten (0x%08lx < 0x%08lx) - disabling it.\n",page_to_pfn(virt_to_page((void *)initrd_start)),min_low_pfn);initrd_start = 0;} #endifsetup_per_cpu_pageset();numa_policy_init();acpi_early_init();if (late_time_init)late_time_init();sched_clock_init();calibrate_delay();pid_idr_init();anon_vma_init(); #ifdef CONFIG_X86if (efi_enabled(EFI_RUNTIME_SERVICES))efi_enter_virtual_mode(); #endifthread_stack_cache_init();cred_init();fork_init();proc_caches_init();uts_ns_init();buffer_init();key_init();security_init();dbg_late_init();vfs_caches_init();pagecache_init();signals_init();seq_file_init();proc_root_init();nsfs_init();cpuset_init();cgroup_init();taskstats_init_early();delayacct_init();poking_init();check_bugs();acpi_subsystem_init();arch_post_acpi_subsys_init();sfi_init_late();/* Do the rest non-__init'ed, we're now alive */arch_call_rest_init();prevent_tail_call_optimization(); }源碼路徑 : linux-5.6.18\init\main.c#822
三、內存初始化源碼 mm_init
mm_init 方法在 linux-5.6.18\init\main.c#795 定義 ,
/** Set up kernel memory allocators*/ static void __init mm_init(void) {/** page_ext requires contiguous pages,* bigger than MAX_ORDER unless SPARSEMEM.*/page_ext_init_flatmem();init_debug_pagealloc();report_meminit();mem_init();kmem_cache_init();kmemleak_init();pgtable_init();debug_objects_mem_init();vmalloc_init();ioremap_huge_init();/* Should be run before the first non-init thread is created */init_espfix_bsp();/* Should be run after espfix64 is set up. */pti_init(); }源碼路徑 : linux-5.6.18\init\main.c#795
四、內存初始化源碼 mem_init
在 linux-5.6.18\init\main.c#795 定義的 mm_init 方法 中 , 調用了 mem_init 方法初始化內存 , 該方法定義在 arch\x86\mm\init_32.c#766 位置 ;
在內存初始化時 , 會打印如下格式的 " 內核空間 內存分布 " 日志 :
printk(KERN_INFO "virtual kernel memory layout:\n"" fixmap : 0x%08lx - 0x%08lx (%4ld kB)\n"" cpu_entry : 0x%08lx - 0x%08lx (%4ld kB)\n" #ifdef CONFIG_HIGHMEM" pkmap : 0x%08lx - 0x%08lx (%4ld kB)\n" #endif" vmalloc : 0x%08lx - 0x%08lx (%4ld MB)\n"" lowmem : 0x%08lx - 0x%08lx (%4ld MB)\n"" .init : 0x%08lx - 0x%08lx (%4ld kB)\n"" .data : 0x%08lx - 0x%08lx (%4ld kB)\n"" .text : 0x%08lx - 0x%08lx (%4ld kB)\n",mem_init 源碼 :
void __init mem_init(void) {pci_iommu_alloc();#ifdef CONFIG_FLATMEMBUG_ON(!mem_map); #endif/** With CONFIG_DEBUG_PAGEALLOC initialization of highmem pages has to* be done before memblock_free_all(). Memblock use free low memory for* temporary data (see find_range_array()) and for this purpose can use* pages that was already passed to the buddy allocator, hence marked as* not accessible in the page tables when compiled with* CONFIG_DEBUG_PAGEALLOC. Otherwise order of initialization is not* important here.*/set_highmem_pages_init();/* this will put all low memory onto the freelists */memblock_free_all();after_bootmem = 1;x86_init.hyper.init_after_bootmem();mem_init_print_info(NULL);printk(KERN_INFO "virtual kernel memory layout:\n"" fixmap : 0x%08lx - 0x%08lx (%4ld kB)\n"" cpu_entry : 0x%08lx - 0x%08lx (%4ld kB)\n" #ifdef CONFIG_HIGHMEM" pkmap : 0x%08lx - 0x%08lx (%4ld kB)\n" #endif" vmalloc : 0x%08lx - 0x%08lx (%4ld MB)\n"" lowmem : 0x%08lx - 0x%08lx (%4ld MB)\n"" .init : 0x%08lx - 0x%08lx (%4ld kB)\n"" .data : 0x%08lx - 0x%08lx (%4ld kB)\n"" .text : 0x%08lx - 0x%08lx (%4ld kB)\n",FIXADDR_START, FIXADDR_TOP,(FIXADDR_TOP - FIXADDR_START) >> 10,CPU_ENTRY_AREA_BASE,CPU_ENTRY_AREA_BASE + CPU_ENTRY_AREA_MAP_SIZE,CPU_ENTRY_AREA_MAP_SIZE >> 10,#ifdef CONFIG_HIGHMEMPKMAP_BASE, PKMAP_BASE+LAST_PKMAP*PAGE_SIZE,(LAST_PKMAP*PAGE_SIZE) >> 10, #endifVMALLOC_START, VMALLOC_END,(VMALLOC_END - VMALLOC_START) >> 20,(unsigned long)__va(0), (unsigned long)high_memory,((unsigned long)high_memory - (unsigned long)__va(0)) >> 20,(unsigned long)&__init_begin, (unsigned long)&__init_end,((unsigned long)&__init_end -(unsigned long)&__init_begin) >> 10,(unsigned long)&_etext, (unsigned long)&_edata,((unsigned long)&_edata - (unsigned long)&_etext) >> 10,(unsigned long)&_text, (unsigned long)&_etext,((unsigned long)&_etext - (unsigned long)&_text) >> 10);/** Check boundaries twice: Some fundamental inconsistencies can* be detected at build time already.*/ #define __FIXADDR_TOP (-PAGE_SIZE) #ifdef CONFIG_HIGHMEMBUILD_BUG_ON(PKMAP_BASE + LAST_PKMAP*PAGE_SIZE > FIXADDR_START);BUILD_BUG_ON(VMALLOC_END > PKMAP_BASE); #endif #define high_memory (-128UL << 20)BUILD_BUG_ON(VMALLOC_START >= VMALLOC_END); #undef high_memory #undef __FIXADDR_TOP#ifdef CONFIG_HIGHMEMBUG_ON(PKMAP_BASE + LAST_PKMAP*PAGE_SIZE > FIXADDR_START);BUG_ON(VMALLOC_END > PKMAP_BASE); #endifBUG_ON(VMALLOC_START >= VMALLOC_END);BUG_ON((unsigned long)high_memory > VMALLOC_START);test_wp_bit(); }總結
以上是生活随笔為你收集整理的【Linux 内核 内存管理】Linux 内核内存布局 ④ ( ARM64 架构体系内存分布 | 内核启动源码 start_kernel | 内存初始化 mm_init | mem_init )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Linux 内核 内存管理】Linux
- 下一篇: 【Linux 内核 内存管理】Linux