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

  start_kernel ——> setup_arch ——> arch_mem_init ——> |——> bootmem_init

                                 |——> device_tree_init

                                 |——> sparse_init

                                   |——> plat_swiotlb_setup

                                 |——> paging_init

  我们看看paging_init做了什么?!

void __init paging_init(void)
{
        unsigned long max_zone_pfns[max_nr_zones];
        unsigned long lastpfn __maybe_unused;
        int i = 0;

        pagetable_init();

#ifdef config_highmem
        kmap_init();
#endif
        kmap_coherent_init();

#ifdef config_zone_dma
        max_zone_pfns[zone_dma] = max_dma_pfn;
#endif
#ifdef config_zone_dma32
        max_zone_pfns[zone_dma32] = max_dma32_pfn;
#endif
        max_zone_pfns[zone_normal] = max_low_pfn;
        lastpfn = max_low_pfn;
#ifdef config_highmem
        max_zone_pfns[zone_highmem] = highend_pfn;
        lastpfn = highend_pfn;
#endif
       /* 上述关于页表初始化就不说了,实在是看不懂!! */                                                                                                                                                                  /*      * 1, max_zone_pfns 是一个数组,MAX_NR_ZONES = 3     *          max_zones_pfns[0] = 131072      : 0  -> ZONE_NORMAL     *          max_zones_pfns[1] = 262144      : 1  -> ZONE_HIGHMEM     *       max_zones_pfns[2] = 2155610112  : 2  -> ZONE_MOVABLE      *   很明显,该数组是UMA系统内存结点的各个内存域的最大PFN.但是ZONE_MOVABLE是一个垃圾值,因为ZONE_MOVEABLE是一个虚拟内存域,而且此时该虚拟内存域的PFN还未计算。     *    还要说明的一点是 max_zone_pfns[0],其实我们系统真正的低端内存的大小是 0 - 57344,此时的 131072 是512M的区域,也就是MIPS默认512M以下都是低端内存!     * 2. free_area_init_nodes    */
       free_area_init_nodes(max_zone_pfns);
}

初始化内存域和节点数据结构

    回忆上篇文章,我们设置了一个数组:early_node_map,到此我们通过基于体系结构相关代码获取了如下信息:

  ① 系统中各个内存域的页帧边界,保存在 max_zone_pfn 数组中。

  ② 各结点页帧的分配情况,保存在全局变量early_node_map中。

1. 管理数据结构的创建

  从内核2.6.10开始提供了一个通用的框架,用于将上述信息转换为伙伴系统预期的结点和内存域数据结构。在这以前,各个体系结构必须自行建立相关数据结构。现在体系结构只需建立简单结构,将繁重的工作交给free_area_init_nodes完成。

/* kernel/mm/page_alloc.c */

/**
 * free_area_init_nodes - Initialise all pg_data_t and zone data
 * @max_zone_pfn: an array of max PFNs for each zone
 *
 * This will call free_area_init_node() for each active node in the system.
 * Using the page ranges provided by add_active_range(), the size of each
 * zone in each node and their holes is calculated. If the maximum PFN
 * between two adjacent zones match, it is assumed that the zone is empty.
 * For example, if arch_max_dma_pfn == arch_max_dma32_pfn, it is assumed
 * that arch_max_dma32_pfn has no pages. It is also assumed that a zone
 * starts where the previous one ended. For example, ZONE_DMA32 starts
 * at arch_max_dma_pfn. * 计算每个内存结点的内存域大小,其中的 holes 也会计算出来!
 */
void __init free_area_init_nodes(unsigned long *max_zone_pfn)
{
    unsigned long nid;
    int i;

    /* Sort early_node_map as initialisation assumes it is sorted */
    sort_node_map();  // 对early_node_map进行排序,后续初始化代码是认为是已经排序过的  /*   * 内核在 lib/sort.c 中提供了一个通用的堆排序实现,该函数采用了这个实现  */

    /* Record where the zone boundaries are */
    memset(arch_zone_lowest_possible_pfn, 0,
                sizeof(arch_zone_lowest_possible_pfn));
    memset(arch_zone_highest_possible_pfn, 0,
                sizeof(arch_zone_highest_possible_pfn));

    arch_zone_lowest_possible_pfn[0] = find_min_pfn_with_active_regions();
    arch_zone_highest_possible_pfn[0] = max_zone_pfn[0];
    /*
      arch_zone_lowest_possible_pfn[0] = 0;
      arch_zone_highest_possible_pfn[0] = 131072;
    */

    for (i = 1; i < MAX_NR_ZONES; i++) {
        if (i == ZONE_MOVABLE)
            continue;
        arch_zone_lowest_possible_pfn[i] =
            arch_zone_highest_possible_pfn[i-1];
        arch_zone_highest_possible_pfn[i] =
            max(max_zone_pfn[i], arch_zone_lowest_possible_pfn[i]);
    }   /*   * arch_zone_lowest_possible_pfn[1] = 131072    * arch_zone_highest_possible_pfn[1] = 262144   */
    arch_zone_lowest_possible_pfn[ZONE_MOVABLE] = 0;
    arch_zone_highest_possible_pfn[ZONE_MOVABLE] = 0;

    /* Find the PFNs that ZONE_MOVABLE begins at in each node */
    memset(zone_movable_pfn, 0, sizeof(zone_movable_pfn));
    find_zone_movable_pfns_for_nodes(zone_movable_pfn);  /*   * 由于ZONE_MOVABLE是一个虚拟内存域,不与真正的硬件内存域关联,该内存域的边界总是设置为0.内核只有在设置了内核命令参数kernelcore或movablecore之一时,该内存域才会存在。   * 该内存域一般开始于各个结点的某个特定内存域的某一页帧号!响应的编号在find_zone_movable_pfns_for_nodes中计算。  */

    /* Print out the zone ranges */
    printk("Zone PFN ranges:\n");
    for (i = 0; i < MAX_NR_ZONES; i++) {
        if (i == ZONE_MOVABLE)
            continue;
        printk("  %-8s ", zone_names[i]);
        if (arch_zone_lowest_possible_pfn[i] ==
                arch_zone_highest_possible_pfn[i])
            printk("empty\n");
        else
            printk("%0#10lx -> %0#10lx\n",
                arch_zone_lowest_possible_pfn[i],
                arch_zone_highest_possible_pfn[i]);
    }

    /* Print out the PFNs ZONE_MOVABLE begins at in each node */
    printk("Movable zone start PFN for each node\n");
    for (i = 0; i < MAX_NUMNODES; i++) {
        if (zone_movable_pfn[i])
            printk("  Node %d: %lu\n", i, zone_movable_pfn[i]);
    }

    /* Print out the early_node_map[] */
    printk("early_node_map[%d] active PFN ranges\n", nr_nodemap_entries);
    for (i = 0; i < nr_nodemap_entries; i++)
        printk("  %3d: %0#10lx -> %0#10lx\n", early_node_map[i].nid,
                        early_node_map[i].start_pfn,
                        early_node_map[i].end_pfn);  /*   * 打印结果:   * Zone PFN ranges:   *   Normal   0x00000000 -> 0x00020000    【0 - 131071】   *   HightMem 0x00020000 -> 0x00040000    【131072 - 262144】   * Movable zone start PFN for each node   【没有开启ZONE_MOVABLE】   * early_node_map[2] active PFN ranges   *   0: 0x00000000 -> 0x0000e000   【0 -> 53744】   *   1: 0x00030000 -> 0x00040000   【196608 -> 262144】  */

    /* Initialise every node */
    mminit_verify_pageflags_layout();
    setup_nr_node_ids();
  /* 遍历各个内存结点,分别调用free_area_init_node创建相关数据结构 */    for_each_online_node(nid) {  // 对于UMA系统,只调用1次
        pg_data_t *pgdat = NODE_DATA(nid);
        free_area_init_node(nid, NULL,
                find_min_pfn_for_node(nid), NULL);

        /* Any memory on that node */
        if (pgdat->node_present_pages)
            node_set_state(nid, N_HIGH_MEMORY);   // 判断该结点是否有内存,如果有,就将结点位图的标志设置为 N_HIGH_MEMORY
        check_for_regular_memory(pgdat);          // 进一步检查地域ZONE_HIGHMEM的内存域中是否有内存,并据此在结点位图中相应地设置为N_NORMAL_MEMORY标志
    }
}
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_id = 0
    pgdat->node_start_pfn = node_start_pfn;               // pgdat->node_start_pfn = 0
    calculate_node_totalpages(pgdat, zones_size, zholes_size);    // 见下文

    alloc_node_mem_map(pgdat);                                    // 见下文
#ifdef CONFIG_FLAT_NODE_MEM_MAP
    printk(KERN_DEBUG "free_area_init_node: node %d, pgdat %08lx, node_mem_map %08lx\n",
        nid, (unsigned long)pgdat,
        (unsigned long)pgdat->node_mem_map);  /*   * free_area_init_node: node 0, pgdat 807874e0, node_mem_map 81000000  */
#endif
    printk("%d : *zones_size = %lu, *zholes_size = %lu", *zones_size, *zholes_size);
    free_area_init_core(pgdat, zones_size, zholes_size);
}
static void __meminit calculate_node_totalpages(struct pglist_data *pgdat,
        unsigned long *zones_size, unsigned long *zholes_size)
{
    unsigned long realtotalpages, totalpages = 0;
    enum zone_type i;

    for (i = 0; i < MAX_NR_ZONES; i++)
        totalpages += zone_spanned_pages_in_node(pgdat->node_id, i,
                                zones_size);
    pgdat->node_spanned_pages = totalpages;

    realtotalpages = totalpages;
    for (i = 0; i < MAX_NR_ZONES; i++)
        realtotalpages -=
            zone_absent_pages_in_node(pgdat->node_id, i,
                                zholes_size);
    pgdat->node_present_pages = realtotalpages;
    printk(KERN_DEBUG "On node %d totalpages: %lu\n", pgdat->node_id,
                            realtotalpages);

  /*   * 计算内存结点的内存域信息:   * pgdat->node_spanned_pages = 262144 【包含 holes】   * pgdat->node_present_pages = 122880 【取出 holes】  */
}
static void __init_refok alloc_node_mem_map(struct pglist_data *pgdat)
{
    /* Skip empty nodes */
    if (!pgdat->node_spanned_pages)   // 跳过不包含内存域的结点
        return;

#ifdef CONFIG_FLAT_NODE_MEM_MAP
    /* ia64 gets its own node_mem_map, before this, without bootmem */
    if (!pgdat->node_mem_map) {     // 系统中的每个物理页帧对应着一个struct page结构体,node_mem_map存储的就是start_page开始的地址
        unsigned long size, start, end;
        struct page *map;

        /*
         * The zone‘s endpoints aren‘t required to be MAX_ORDER
         * aligned but the node_mem_map endpoints must be in order
         * for the buddy allocator to function correctly.
         */
        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);
        /*      * start = 0, end = 2621144, size = 8388608  【说明在创建 struct page 实例时,包含了hole也一起创建了 struct page】     */
        map = alloc_remap(pgdat->node_id, size); // 如果特定于体系结构的代码尚未建立内存映射,返回NULL
        if (!map)
            map = alloc_bootmem_node_nopanic(pgdat, size);  // 使用bootmem allocator分配器进行内存分配
        pgdat->node_mem_map = map + (pgdat->node_start_pfn - start);
    }
#ifndef CONFIG_NEED_MULTIPLE_NODES
    /*
     * With no DISCONTIG, the global mem_map is just set as node 0‘s
     */
    if (pgdat == NODE_DATA(0)) {
        mem_map = NODE_DATA(0)->node_mem_map;
#ifdef CONFIG_ARCH_POPULATES_NODE_MAP
        if (page_to_pfn(mem_map) != pgdat->node_start_pfn)
            mem_map -= (pgdat->node_start_pfn - ARCH_PFN_OFFSET);
#endif /* CONFIG_ARCH_POPULATES_NODE_MAP */
    }
#endif
#endif /* CONFIG_FLAT_NODE_MEM_MAP */
}

  函数 free_area_init_core对 pgdata 中相关的数据结构进行初始化设置。

时间: 2024-10-11 14:52:27

内存管理初始化源码5:free_area_init_nodes的相关文章

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

我们在阅读源码时,函数功能可以分为两类:1. bootmem.c 2. page_alloc.c. 1. bootmem.c是关于bootmem allocator的,上篇文章已经简述过. 2. page_alloc.c是关于Memory Management subsystem的. 关于内存管理子系统的初始化调用了多个函数,我们首先分析在bootmem_init中调用的add_active_range函数. start_kernel --> setup_arch ---> arch_mem_

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

源码声明:基于Linux kernel 3.08 1. 在kernel/arch/mips/kernel/head.S中会做一些特定硬件相关的初始化,然后会调用内核启动函数:start_kernel: 2. start_kernel是通用的内核启动函数,但是在初始化内核过程中,必然有一些参数是特定于硬件体系结构的,这些特定于硬件体系结构的设置通过调用函数setup_arch函数: 3. 我们看看MIPS架构的setup_arch函数做了哪些特定于MIPS的设置: /* kernel/arch/m

内存管理初始化源码3:bootmem

start_kernel ——> setup_arch ——> arch_mem_init ——> bootmem_init ——> init_bootmem_node: 此时,不得不说的就是 bootmem . 1. 什么是bootmem: 我们都知道,所有的物理内存是交给内核管理的,或者说是交给内存管理子系统管理的.那么,从内核启动到内核管理子系统启动之间,是否需要内存呢?答案是肯定的,该时间段内是需要物理内存的. 那么bootmem就是负责该时间段的物理内存的分配. 2. 特

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

PFN相关宏说明: /* kernel/include/linux/pfn.h */ PFN : Page Frame Number(物理页帧) /* * PFN_ALIGN:返回地址x所在那一页帧的下一页帧的起始地址. * 例如:PFN_ALIGN(0x00000800) = 0x00001000 : PFN_ALIGN(0x00001800) = 0x00002000; * 理解:假如我们认为一页大小是0x0f,那么当前地址是0x08,如何通过0x08获得0x10呢? 0x08 + (0x1

Python学习第六天----Linux内存管理、进程管理、RPM包安装管理及源码安装软件

Linux内存管理.进程管理.RPM包安装管理及源码安装软件 一.交换分区     交换分区其实就相当于Windows系统下的虚拟内存的概念,当物理内存不够用的时候,由操作系统将硬盘的一块区域划分出来作为内存使用.具体使用方法如下:      [[email protected] ~]# fdisk -l 磁盘 /dev/sdb:16.1 GB, 16106127360 字节,31457280 个扇区 Units = 扇区 of 1 * 512 = 512 bytes 扇区大小(逻辑/物理):5

Jedis cluster集群初始化源码剖析

Jedis cluster集群初始化源码剖析 环境 jar版本: spring-data-redis-1.8.4-RELEASE.jar.jedis-2.9.0.jar 测试环境: Redis 3.2.8,八个集群节点 applicationContext-redis-cluster.xml 配置文件: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.spri

用户、组管理,源码包安装

先讲解后试验,最后有截图文件 一.用户账户管理 1. 用户账户: 超级用户root(0) 程序用户(1~499) 普通用户(500~60000) 组账户: 基本组(私有组) 附加组(公共组) 2. /etc/passwd 保存用户账户的基本信息 # grep ^root /etc/passwd root:x:0:0:root:/root:/bin/bash 字段1:帐号名root 字段2:密码占位符x 字段3:UID 字段4:GID 字段5:用户全名 字段6:宿主目录 字段7:登录Shell  

软件包管理之源码编译安装

编译步骤: 1.#cd SOURCE_CODE 2../configure 作用: 检测编译环境 提供给用户编译配置(通过脚本选项) --help 帮助信息 --prefix=/usr/local/name 安装路径 二进制 /usr/local/name/bin /usr/local/name/libexec不允许自动执行,只能被其他程序调用执行 配置文件 /usr/local/name/conf 库文件 /usr/local/name/lib 帮助文档 /usr/local/name/sha

MapReduce job在JobTracker初始化源码级分析

mapreduce job提交流程源码级分析(三)中已经说明用户最终调用JobTracker.submitJob方法来向JobTracker提交作业.而这个方法的核心提交方法是JobTracker.addJob(JobID jobId, JobInProgress job)方法,这个addJob方法会把Job提交到调度器(默认是JobQueueTaskScheduler)的监听器JobQueueJobInProgressListener和EagerTaskInitializationListen