• Linux内核源码分析之setup_arch (二)


    1. 概述

    接着上一篇《Linux内核源码分析之setup_arch (一)》继续分析,本文首先分析arm_memblock_init函数,然后分析内核启动阶段的是如何进行内存管理的。

    2. arm_memblock_init

    该函数的功能比较简单,主要就是把meminfo中记录的内存条信息添加到memblock.memory中,然后把内核镜像所在内存区域添加到memblock.reserved中,arm_mm_memblock_reserve把页表所在内存区域添加到memblock.reserved中;如果使用了设备树,则使用arm_dt_memblock_reserve来保留所占用的内存,最后则是调用CPU相关的mdesc->reserve,其对应的调用为cpu_mem_reserve,该函数定义在cpu.c中。

    /* arch/arm/mm/init.c */
    void __init arm_memblock_init(...) {
      for (i = 0; i < mi->nr_banks; i++)
        memblock_add(mi->bank[i].start, mi->bank[i].size);
    
      memblock_reserve(__pa(_stext), _end - _stext);
      arm_mm_memblock_reserve();
      arm_dt_memblock_reserve();
    
      if (mdesc->reserve)
        mdesc->reserve();
    
      arm_memblock_steal_permitted = false;
      memblock_allow_resize();
      memblock_dump_all();
    }
    /* include/kernel/memblock.h */
    struct memblock {
     phys_addr_t current_limit;
     struct memblock_type memory;
     struct memblock_type reserved;
    };
    

    3. memblock_alloc

    接下来就该执行paging_init函数了,在分析paging_init之前先来点内核启动阶段的内存管理相关的内容。从arm_memblock_init开始引入memblock数据结构,其作用是实现内核启动初期的内存管理功能,严格来说,其生命周期到paging_init::bootmem_init为止,memblock_alloc调用流程如下。

    实际查找空闲内存的函数为memblock_find_in_range_node,而该函数中真正实现空闲内存查找的是for_each_free_mem_range_reverse这个宏定义。

    /* mm/memblock.c */
    phys_addr_t memblock_find_in_range_node(...)
    {
     ...
     for_each_free_mem_range_reverse(i, nid, &this_start, &this_end, NULL) {
          ...
          if (cand >= this_start)
            return cand;
     }
     return 0;
    }
    

    该宏定义如下,然而其中又嵌套了一个函数Orz...

    /* include/linux/memblock.h */
    #define for_each_free_mem_range_reverse(i, nid, p_start, p_end, p_nid) 
     for (i = (u64)ULLONG_MAX,                                             
          __next_free_mem_range_rev(&i, nid, p_start, p_end, p_nid);       
          i != (u64)ULLONG_MAX;                                            
          __next_free_mem_range_rev(&i, nid, p_start, p_end, p_nid))
    

    首先需要说明的是,memblock.reserved标识的区域表示的是已被占用的内存区域,memblock.memory中记录的是内存条信息。现在回到__next_free_mem_range_rev函数,代码段(1)(2)的目的是找出内存条上两个reserved区域之间的内存区域,即空闲区域。找到之后再经过代码段(3)对空闲区域的起始地址和结束地址进行修正,因为代码段(1)(2)只能保证空闲区与当前内存条存在交集,并不能保证该空闲区域完全处于当前内存条之中,主要原因在于无法保证这两个reserved区域都在当前内存条上。

    /* mm/memblock.c */
    void __init_memblock __next_free_mem_range_rev(...)
    {
     struct memblock_type *mem = &memblock.memory;
     struct memblock_type *rsv = &memblock.reserved;
     ...
     /* (1) */
     for ( ; mi >= 0; mi--) {
      struct memblock_region *m = &mem->regions[mi];
      phys_addr_t m_start = m->base;
      phys_addr_t m_end = m->base + m->size;
      ...
      /* (2) */
      for ( ; ri >= 0; ri--) {
       struct memblock_region *r = &rsv->regions[ri];
       phys_addr_t r_start = ri ? r[-1].base + r[-1].size : 0;
       phys_addr_t r_end = ri < rsv->cnt ? r->base : ULLONG_MAX;
       ...
       /* (3) */
       if (m_end > r_start) {
        if (out_start)
         *out_start = max(m_start, r_start);
        if (out_end)
         *out_end = min(m_end, r_end);
        if (out_nid)
         *out_nid = memblock_get_region_node(m);
    
        ...
        return;
       }
      }
     }
    
     *idx = ULLONG_MAX;
    }
    

    至此,空闲区域的查找基本就结束了,回到memblock_find_in_range_node函数中,再检查一下该区域的起始地址和结束地址是否合法等等,最终就申请到了所请求大小的内存区域,最后只需要将这块内存区域标记为reserved状态就结束了内存分配的整个过程了。

    /* mm/memblock.c */
    int memblock_reserve(phys_addr_t base, phys_addr_t size)
    {
     struct memblock_type *_rgn = &memblock.reserved;
     return memblock_add_region(_rgn, base, size, MAX_NUMNODES);
    }
    

    4. 总结

    • arm_memblock_init函数首先把记录在meminfo记录的内存条信息转移到memblock.memory中,然后把已经使用的内存区域记录到memblock.reserved中,主要包括内核镜像所占用区域、页表区域以及设备树;
    • memblock_alloc通过memblock中的memory和reserved中记录的信息进行内存管理,每次申请到内存之后都在memblock.reserved中进行记录。
  • 相关阅读:
    向量的内积和外积
    软件姿态解算
    PLC与外接按钮开关接线方法图解
    关于三极管处于临界饱和状态的分析
    C语言中的volatile——让我保持原样
    C语言异或运算在程序设计中的妙用
    各种转PDF
    javax.mail发送邮件功能
    MySQL总结
    idea永久破解
  • 原文地址:https://www.cnblogs.com/jiau/p/14106361.html
Copyright © 2020-2023  润新知