zoukankan      html  css  js  c++  java
  • Linux内核源码分析之setup_arch (四)

    前言

    Linux内核源码分析之setup_arch (三) 基本上把setup_arch主要的函数都分析了,由于距离上一篇时间比较久了,所以这里重新贴一下大致的流程图,本文主要分析的是bootmem_init函数。

    代码分析

    bootmem_init函数的结构如下:

    find_limits通过存储在meminfo中的内存条信息得到低端内存和高端内存的页框编号,分别放入到min、max_low、max_high中。

    static void __init find_limits(unsigned long *min, unsigned long *max_low,
              unsigned long *max_high)
    {
     ...
     *min = bank_pfn_start(&mi->bank[0]);
     for_each_bank (i, mi)
      if (mi->bank[i].highmem)
        break;
     *max_low = bank_pfn_end(&mi->bank[i - 1]);
     *max_high = bank_pfn_end(&mi->bank[mi->nr_banks - 1]);
    }
    

    arm_bootmem_init对低端内存区域进行管理,流程图如下:

    在通过find_limits得到内存的起止页框号之后,通过bootmem_bootmap_pages计算得到需要分配bitmap的大小,分配好bitmap之后调用init_bootmem_node将起止页框号和bitmap信息写入到pgdat中。

    /* arch/arm/mm/init.c */
    static void __init arm_bootmem_init(unsigned long start_pfn,
     unsigned long end_pfn)
    {
     ...
     boot_pages = bootmem_bootmap_pages(end_pfn - start_pfn);
     bitmap = memblock_alloc_base(boot_pages << PAGE_SHIFT, L1_CACHE_BYTES,
        __pfn_to_phys(end_pfn));
    
     node_set_online(0);
     pgdat = NODE_DATA(0);
     init_bootmem_node(pgdat, __phys_to_pfn(bitmap), start_pfn, end_pfn);
      ...
    }
    

    最后就是把memblock管理的内存移交给bootmem来管理,对于memblock中的空闲区域通过free_bootmem将bitmap中对应的bit置零,而已经使用的内存,即memblock中对应的reserved的区域使用reserve_bootmem将bitmap中对应bit置1。

    /* arch/arm/mm/init.c */
    static void __init arm_bootmem_init(...)
    {
     ...
     /* Free the lowmem regions from memblock into bootmem. */
     for_each_memblock(memory, reg) {
      ...
      free_bootmem(__pfn_to_phys(start), (end - start) << PAGE_SHIFT);
     }
     /* Reserve the lowmem memblock reserved regions in bootmem. */
     for_each_memblock(reserved, reg) {
      ...
      reserve_bootmem(__pfn_to_phys(start),
               (end - start) << PAGE_SHIFT, BOOTMEM_DEFAULT);
     }
    }
    

    在设置好bitmap之后,接下来开始分配对应的page结构体,在分配page结构体内存之前,先统计出各个zone区域内的内存空洞,存放在zhole_size中。

    /* arch/arm/mm/init.c */
    static void __init arm_bootmem_free(unsigned long min, unsigned long max_low,
     unsigned long max_high)
    {
     ...
     for_each_memblock(memory, reg) {
      ...
      if (start < max_low) {
       unsigned long low_end = min(end, max_low);
       zhole_size[0] -= low_end - start;
      }
    #ifdef CONFIG_HIGHMEM
      if (end > max_low) {
       unsigned long high_start = max(start, max_low);
       zhole_size[ZONE_HIGHMEM] -= end - high_start;
      }
    #endif
     }
     ...
     free_area_init_node(0, zone_size, min, zhole_size);
    }
    

    统计好内存空洞之后开始分配page结构体所需要的内存空间,大致流程如下:

    calculate_node_totalpages通过zones_size和zholes_size计算出内存页总数和真正可用的内存页数量,分别记录在pgdat->node_spanned_pages和pgdat->node_present_pages中。

    /* mm/page_alloc.c */
    void __paginginit free_area_init_node(int nid, unsigned long *zones_size,
      unsigned long node_start_pfn, unsigned long *zholes_size)
    {
     pg_data_t *pgdat = NODE_DATA(nid);
    
     pgdat->node_id = nid;
     pgdat->node_start_pfn = node_start_pfn;
     calculate_node_totalpages(pgdat, zones_size, zholes_size);
    
     alloc_node_mem_map(pgdat);
     free_area_init_core(pgdat, zones_size, zholes_size);
    }
    

    alloc_node_mem_map根据pgdat->node_spanned_pages的大小确定需要分配的page结构体数量,这其中包括了内存空洞部分的区域,分配好之后将起始地址记录到pgdat->node_mem_map中,同时也记录在全局变量mem_map中。

    /* mm/page_alloc.c */
    static void __init_refok alloc_node_mem_map(struct pglist_data *pgdat)
    {
     ...
     if (!pgdat->node_mem_map) {
      ...
      start = pgdat->node_start_pfn & ~(MAX_ORDER_NR_PAGES - 1);
      end = pgdat->node_start_pfn + pgdat->node_spanned_pages;
      end = ALIGN(end, MAX_ORDER_NR_PAGES);
      size =  (end - start) * sizeof(struct page);
      map = alloc_remap(pgdat->node_id, size);
      if (!map)
       map = alloc_bootmem_node_nopanic(pgdat, size);
      pgdat->node_mem_map = map + (pgdat->node_start_pfn - start);
     }
    
     if (pgdat == NODE_DATA(0))
      mem_map = NODE_DATA(0)->node_mem_map;
     ...
    }
    

    最后通过free_area_init_core初始化page结构体,大致流程如下:

    主要作用是设置每个zone结构体的信息,比如zone的可用空间大小等信息,并把每个zone的page结构体初始化,记录自己所属的zone和node_id,同时把page结构体状态设置为PG_reserved,这里是无差别的设置的,至于空闲的内存页会在后续的mm_init::mem_init::free_all_bootmem中重新释放出来;另外,SetPageReserved是通过宏声明的,所以是无法找到该函数的,其定义在page-flags.h中。

    总结

    bootmem_init函数的作用是分配bitmap和page结构体所需要的空间,同时把已使用的和空闲的内存区域都标记到bitmap中,然后更新每个zone的内存信息,并把属于每个zone内存空间对应的page结构体进行初始化,且全部都设置为PG_reserved状态。

  • 相关阅读:
    一个例子讲明爬虫请求库requests
    SpringBoot【六】 Shiro
    SpringBoot【五】 Spring Security
    SpringBoot【四】 任务
    Swagger
    SpringBoot【三】 整合 JDBC、Druid、MyBatis
    SpringBoot【二】 SpringBoot 配置
    SpringBoot【一】入门程序及原理分析
    SpingBoot 相关问题:启动 web 项目之后出现 java.lang.UnsatisfiedLinkError: D:Tomcatapache-tomcat-9.0.36in cnative-1.dll
    SpringBoot 相关问题:IDEA 创建 SpringBoot 项目时出现 Initialization failed for 'https://start.spring.io' 问题
  • 原文地址:https://www.cnblogs.com/jiau/p/14588409.html
Copyright © 2011-2022 走看看