start_kernel ——> setup_arch ——> arch_mem_init ——> bootmem_init ——> init_bootmem_node:

  此时,不得不说的就是 bootmem 。

1. 什么是bootmem:

  我们都知道,所有的物理内存是交给内核管理的,或者说是交给内存管理子系统管理的。那么,从内核启动到内核管理子系统启动之间,是否需要内存呢?答案是肯定的,该时间段内是需要物理内存的。

  那么bootmem就是负责该时间段的物理内存的分配。

2. 特性:

  简单

  该分配器的需求集中于简单性方面,而不是性能和通用性。因此内核开发者决定实现一个最先适配(first-fit)分配器用于在启动阶段管理内存。

3. 基本原理

  用一个位图来管理页,位图比特位的数目与系统中物理内存页的数据相同。比特位为1,表示已用页;比特位为0,表示空闲页。

  在需要分配内存时,分配器逐位扫描位图,直至找到一个能够提供足够连续页的位置,即所谓的最先最佳(first-best)或最先适配的位置。

4. 初始化

/**
* init_bootmem_node - register a node as boot memory
* @pgdat: node to register                        【属于某个内存结点的bootmem】
* @freepfn: pfn where the bitmap for this node is to be placed   【该内存结点的物理内存页的位图所存内存的pfn】
* @startpfn: first pfn on the node                   【该内存的结点的 first pfn】
* @endpfn: first pfn after the node               【该内存将诶点的 end pfn】
*
* Returns the number of bytes needed to hold the bitmap for this node. 【返回管理该结点所有内存页的位图所需的总字节数】
*/
unsigned long __init init_bootmem_node(pg_data_t *pgdat, unsigned long freepfn,
unsigned long startpfn, unsigned long endpfn)
{
return init_bootmem_core(pgdat->bdata, freepfn, startpfn, endpfn);
}
/**
* 我们传递的参数是: bootmap_size = init_bootmem_node(NODE_DATA(0), mapstart, min_low_pfn, max_low_pfn);
* mapstart = 2358, min_low_pfn = 0, max_low_pfn = 131072
* 注意:1. mapstart之前的页是存储了initrd相关数据,上文已经解释过
* 2. max_low_pfn 为131072是由于此时是将 0 ~ 512M 都认为是低端内存,将 0 ~ 512M 之间的所有物理页都建立了bitmap,其实,我们的低端物理内存页只有 0 ~ 57344
*/
/*
* Called once to set up the allocator itself.
*/
static unsigned long __init init_bootmem_core(bootmem_data_t *bdata,
unsigned long mapstart, unsigned long start, unsigned long end)
{
unsigned long mapsize; mminit_validate_memmodel_limits(&start, &end);         // start 和 end 的合法性检测
bdata->node_bootmem_map = phys_to_virt(PFN_PHYS(mapstart)); // mapstart是存储位图的pfn,转换为相应的虚拟地址
bdata->node_min_pfn = start;                   // 记录 node_min_pfn
bdata->node_low_pfn = end;                     // 记录 node_low_pfn
link_bootmem(bdata);                        // bdata和什么做关联? /*
* Initially all pages are reserved - setup_arch() has to
* register free RAM areas explicitly.
*/
  // 初始化所有保留的页——setup_arch()必须精确的注册所有的RAM区域
mapsize = bootmap_bytes(end - start);             // 计算 bitmap 所需的 bytes
memset(bdata->node_bootmem_map, 0xff, mapsize);     // 将 bitmap 区域设置 0xff bdebug("nid=%td start=%lx map=%lx end=%lx mapsize=%lx\n",
bdata - bootmem_node_data, start, mapstart, end, mapsize); return mapsize;
}
...
static struct list_head bdata_list __initdata = LIST_HEAD_INIT(bdata_list);
... /*
* link bdata in order  【将bdata按顺序连接到临时链表 bdata_list中】
*/
static void __init link_bootmem(bootmem_data_t *bdata)
{
struct list_head *iter; list_for_each(iter, &bdata_list) {
bootmem_data_t *ent; ent = list_entry(iter, bootmem_data_t, list);
if (bdata->node_min_pfn < ent->node_min_pfn)
break;
}
list_add_tail(&bdata->list, iter);   /* 等价于 */
  for (iter = (&bdata_list)->next; iter != (&bdata_list); iter = iter->next) {
    bootmem_data_t *ent;
    ent = container_of(iter, bootmem_data_t, list);
    if (bdata->node_min_pfn < ent->node_min_pfn)
      break;
  }
  list_add_tail(&bdata->list, iter); // 将该结点的 bdata 连接到临时链表 bdata_list中, bdata_list的定义在 bootmem.c中
}
static unsigned long __init bootmap_bytes(unsigned long pages)
{
unsigned long bytes = (pages + ) / ; return ALIGN(bytes, sizeof(long));
}

5. 将低端内存交给 bootmem allocator 管理

/**
* free_bootmem - mark a page range as usable
* @addr: starting address of the range
* @size: size of the range in bytes
*
* Partial pages will be considered reserved and left as they are.
*
* The range must be contiguous but may span node boundaries.
*/
/**
* 功能:标志一个 page 为可用状态
* @addr : 标记范围的开始地址
* @size : 标记范围的大小(bytes)
* 范围必须是连续的,但可能跨节点的边界。
*/
void __init free_bootmem(unsigned long addr, unsigned long size)
{
unsigned long start, end; kmemleak_free_part(__va(addr), size); // 内核内存泄露检测 start = PFN_UP(addr);
end = PFN_DOWN(addr + size); mark_bootmem(start, end, , );
}
/**
* free_bootmem(PFN(start), size << PAGE_SHIFT);   // start = 2358, size = end - start = 53744 - 2358
* 虽然我们在建立位图时建立的是整个低端内存的位图,但其实我们真正可用的物理内存只有这些。
*/
static int __init mark_bootmem(unsigned long start, unsigned long end,
int reserve, int flags)
{
unsigned long pos;
bootmem_data_t *bdata; pos = start;
list_for_each_entry(bdata, &bdata_list, list) {
int err;
unsigned long max; if (pos < bdata->node_min_pfn ||
pos >= bdata->node_low_pfn) {
BUG_ON(pos != start);
continue;
} max = min(bdata->node_low_pfn, end); // 此时:bdata->node_low_pfn = 131072(对应512M), end = 57344(对应256M) err = mark_bootmem_node(bdata, pos, max, reserve, flags);
if (reserve && err) {
mark_bootmem(start, pos, , );
return err;
} if (max == end)
return ;
pos = bdata->node_low_pfn;
}
BUG();
}
static int __init mark_bootmem_node(bootmem_data_t *bdata,
unsigned long start, unsigned long end,
int reserve, int flags)
{
unsigned long sidx, eidx; bdebug("nid=%td start=%lx end=%lx reserve=%d flags=%x\n",
bdata - bootmem_node_data, start, end, reserve, flags); BUG_ON(start < bdata->node_min_pfn);
BUG_ON(end > bdata->node_low_pfn); printk("%d : start = %lu, end = %lu\n", __LINE__, start, end); sidx = start - bdata->node_min_pfn; // start = 2350, bdata->node_min_pfn = 0
eidx = end - bdata->node_min_pfn; // end = 53744, bdata->node_min_pfn = 0 printk("%d : sidx = %lu, eidx = %lu\n", __LINE__, sidx, eidx); if (reserve)
return __reserve(bdata, sidx, eidx, flags); // 此时的 _reserve 和 _free 猜测就可知, _reserve 是 set bit, _free 是 clear bit.
else
__free(bdata, sidx, eidx);
return ;
}

  上述代码从细节上分析了 bootmem 的初始化及如何将低端内存交给 bootmem 管理; 那么如何分配内存及释放内存稍后解释,我们从宏观看看 bootmem 的这些函数。

1. 初始化:

  init_bootmem_node:初始化某个内存结点的 bootmem,具体是哪个内存结点由使用者指定。用于初始化具有多个内存结点的系统(NUMA)。

  init_bootmem          :初始化内存结点为0的 bootmem。用于初始化只有一个内存结点的系统。只能用于初始化(UMA),而 init_bootmem_node也可以初始化UMA。

2. 释放:

  free_all_bootmem_node:释放某个内存结点的内存给 buddy allocator(NUMA)

  free_all_bootmem          :释放空闲页给 buddy allocator(UMA)

  首先扫描 bootmem 分配器的页位图,释放每个未用的页,到伙伴系统的接口是 __free_pages_bootmem函数,该函数对每个空闲页调用。该函数内部依赖于标准函数 __free_page。它使得这些页并入伙伴系统的数据结构,在其中作为空闲页管理,可用于分配。

  在位图已经完全扫描之后,它占据的内存空间也必须释放。此后,只有伙伴系统可以分配内存。

内存管理初始化源码3:bootmem的更多相关文章

  1. 内存管理初始化源码4:add_active_range

    我们在阅读源码时,函数功能可以分为两类:1. bootmem.c 2. page_alloc.c. 1. bootmem.c是关于bootmem allocator的,上篇文章已经简述过. 2. pa ...

  2. 内存管理初始化源码1:setup_arch

    源码声明:基于Linux kernel 3.08 1. 在kernel/arch/mips/kernel/head.S中会做一些特定硬件相关的初始化,然后会调用内核启动函数:start_kernel: ...

  3. 内存管理初始化源码2:setup_arch

    PFN相关宏说明: /* kernel/include/linux/pfn.h */ PFN : Page Frame Number(物理页帧) /* * PFN_ALIGN:返回地址x所在那一页帧的 ...

  4. 内存管理初始化源码5:free_area_init_nodes

    start_kernel ——> setup_arch ——> arch_mem_init ——> |——> bootmem_init  |——> device_tree ...

  5. C++动态内存管理与源码剖析

    引言 在本篇文章中,我们主要剖析c++中的动态内存管理,包括malloc.new expression.operator new.array new和allocator内存分配方法以及对应的内存释放方 ...

  6. 内存管理 初始化(二)bootmem位图分配器建立 及 使用

    本地的笔记有点长,先把bootmem位图分配器的建立 及  使用过程做下梳理. 都是代码,上面做了标注.开始的汇编部分省略了(涉及的内容不多,除了swapper_pg_dir的分配). 该记录不会再添 ...

  7. Jedis cluster集群初始化源码剖析

    Jedis cluster集群初始化源码剖析 环境 jar版本: spring-data-redis-1.8.4-RELEASE.jar.jedis-2.9.0.jar 测试环境: Redis 3.2 ...

  8. C#共享内存实例 附源码

    原文 C#共享内存实例 附源码 网上有C#共享内存类,不过功能太简单了,并且写内存每次都从开头写.故对此进行了改进,并做了个小例子,供需要的人参考. 主要改进点: 通过利用共享内存的一部分空间(以下称 ...

  9. 内存管理 初始化(八) 至kswapd_init

    至此,内存初始化部分已看完,遗留问题: 1.对于unicore或者mips的页表建立都很清楚,但是对于ARM我不清楚: 初始化部分涉及的页表映射建立,我都以unicore架构为准,ARM的页表映射从原 ...

随机推荐

  1. 【luogu1613】跑路 - 倍增+Floyd

    题目描述 小A的工作不仅繁琐,更有苛刻的规定,要求小A每天早上在6:00之前到达公司,否则这个月工资清零.可是小A偏偏又有赖床的坏毛病.于是为了保住自己的工资,小A买了一个十分牛B的空间跑路器,每秒钟 ...

  2. idea Maven项目 包下载不下来或者已经下载了就是飘红

    0.先在settings.xml加上阿里的镜像在刷新试试 <mirror> <id>aliyunmaven</id> <mirrorOf>*</m ...

  3. JavaScript学习系列博客_2_JavaScript编写位置

    JavaScript代码一般编写在哪里呢? 看到这个问题,第一个反应就是,JavaScript那当然是编写在<Script></Script>这对标签中啊!然而! 1.可以编写 ...

  4. Dubbo源码学习之-通过源码看看dubbo对netty的使用

    前言 前段时间,从头开始将netty源码了解了个大概,但都是原理上理解.刚好博主对dubbo框架了解过一些,这次就以dubbo框架为例,详细看看dubbo这种出色的开源框架是如何使用netty的,又是 ...

  5. 2020.5.23 第三篇 Scrum冲刺博客

    Team:银河超级无敌舰队 Project:招新通 项目冲刺集合贴:链接 目录 一.每日站立会议 二.项目燃尽图 三.签入记录 3.1 代码/文档签入记录 3.2 主要代码截图 3.3 程序运行截图 ...

  6. ReplayingDecoder 解码器:别以为我有多厉害,也只不过是使用了一下装饰器模式而已~

    原文地址 一.设计模式为啥老是用不好? 想要写出更屌的代码,提高代码的健壮性和可扩展性,那么设计模式可谓是必学的技能. 关于学习设计模式,大家可能都觉得设计模式的概念太过于抽象,理解起来有点费劲:又或 ...

  7. 学习lammps 对in文件的一个概述性心得(转载)

    转载自:http://muchong.com/html/201411/8149677.html 写在开头:1.尽量列举了大部分(几乎)的命令2.带星号命令非常重要,大家在看mannual中命令的解释的 ...

  8. HttpServletRespnse 对象 相关基本应用

    HttpServletRespnse 对象相关基本应用 向浏览器输出数据 getOutputStream() @Override protected void service(HttpServletR ...

  9. Nodejs模块:querystring

    /** * @description querystring模块 */ const querystring = require('querystring'); const url = 'https:/ ...

  10. php实现无限极分类(多维数组 / 二维数组)形式

    <?php // 测试数组数据$array = array( array('id'=>'1','title'=>'父级分类1','pid'=>'0'), array('id'= ...