前言

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状态。

Linux内核源码分析之setup_arch (四)的更多相关文章

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

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

  2. Linux内核源码分析之setup_arch (三)

    1. 前言 在 Linux内核源码分析之setup_arch (二) 中介绍了当前启动阶段的内存分配函数memblock_alloc,该内存分配函数在本篇将要介绍paging_init中用于页表和内存 ...

  3. Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7)【转】

    原文地址:Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.c ...

  4. Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7)

    http://blog.chinaunix.net/uid-20543672-id-3157283.html Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3 ...

  5. Linux内核源码分析 day01——内存寻址

    前言 Linux内核源码分析 Antz系统编写已经开始了内核部分了,在编写时同时也参考学习一点Linux内核知识. 自制Antz操作系统 一个自制的操作系统,Antz .半图形化半命令式系统,同时嵌入 ...

  6. Linux内核源码分析方法_转

    Linux内核源码分析方法 转自:http://www.cnblogs.com/fanzhidongyzby/archive/2013/03/20/2970624.html 一.内核源码之我见 Lin ...

  7. Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7)【转】

    原文地址:Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://bl ...

  8. Linux内核源码分析方法

    一.内核源码之我见 Linux内核代码的庞大令不少人“望而生畏”,也正因为如此,使得人们对Linux的了解仅处于泛泛的层次.如果想透析Linux,深入操作系统的本质,阅读内核源码是最有效的途径.我们都 ...

  9. 【转】Linux内核源码分析方法

    一.内核源码之我见 Linux内核代码的庞大令不少人“望而生畏”,也正因为如此,使得人们对Linux的了解仅处于泛泛的层次.如果想透析Linux,深入操作系统的本质,阅读内核源码是最有效的途径.我们都 ...

随机推荐

  1. SwiftUI All In One

    SwiftUI All In One SwiftUI SwiftUI is an innovative, exceptionally simple way to build user interfac ...

  2. ES6 Map vs ES5 Object

    ES6 Map vs ES5 Object Map vs Object https://developer.mozilla.org/en-US/docs/Web/JavaScript/Referenc ...

  3. bob and brad physical therapy knee exercise

    bob and brad physical therapy knee exercise 鲍勃和布拉德物理治疗膝关节运动 https://bobandbrad.com/ youtube https:// ...

  4. full page screen capture in js

    full page screen capture in js html2canvas https://html2canvas.hertzen.com/ https://github.com/nikla ...

  5. DMCA Takedown Policy

    DMCA Takedown Policy https://github.com/xgqfrms/xgqfrms/issues/46 https://help.github.com/en/github/ ...

  6. windows driver 简单的驱动和通信

    sysmain.c #pragma once #pragma warning(disable: 4100) #include <ntifs.h> #include <ntddk.h& ...

  7. nasm astrlen函数 x86

    xxx.asm %define p1 ebp+8 %define p2 ebp+12 %define p3 ebp+16 section .text global dllmain export ast ...

  8. JDK源码阅读-ByteBuffer

    本文转载自JDK源码阅读-ByteBuffer 导语 Buffer是Java NIO中对于缓冲区的封装.在Java BIO中,所有的读写API,都是直接使用byte数组作为缓冲区的,简单直接.但是在J ...

  9. el-input输入框的readonly属性

    readonly属性是Boolean类型,默认值为false.readonly值为true表示只读. <el-col :span="12"> <el-form-i ...

  10. 关于Java中for,while,if,方法的练习

    练习 计算0到100之间的奇数和偶数和 package com.kangkang.forDemo;​public class demo01 {    public static void main(S ...