内存管理初始化源码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 + (0x10 - 1) = 0x17, 然后再把低位抹掉,不就刚好是10. * 问题:这样做有一个问题,如果x=0x0,那么返回的是0x0,而不是0x10,这是为什么呢?*/#define PFN_ALIGN(x)    (((unsigned long)(x) + (PAGE_SIZE - 1)) & PAGE_MASK)

/* * PFN_UP:获取地址x所在物理页帧的的后一个PFN值,即x如果属于page 0, 则返回1. * 问题:如果 x = 0x0, 那么返回的不是1,而是0;如果 x = 0x00001000,返回的是1,而不是2.为什么?*/#define PFN_UP(x)    (((x) + PAGE_SIZE-1) >> PAGE_SHIFT)

/* * PFN_DOWN:获取地址x所在物理页帧的前一个PFN值,即如果x属于page 1,则返回0.*/#define PFN_DOWN(x)    ((x) >> PAGE_SHIFT)
/* * PFN_PHYS:返回PFN值为x时,对应的物理页帧的起始地址*/#define PFN_PHYS(x)    ((phys_addr_t)(x) << PAGE_SHIFT)

关于上述问题,其实如果看看它的实际应用场景就会明白的。要记住:PFN是一个从0开始的页帧编号

  打印结果记录:

    initrd_start = 0x81a0000, initrd_end = 0x81b2e720  

  我们继续看 start_kernel—>setup_arch—>arch_mem_init—>bootmem_init。

/* kernel/arch/mips/kernel/setup.c */
static void __init bootmem_init(void)
{
    unsigned long reserved_end;
    unsigned long mapstart = ~0UL;
    unsigned long bootmap_size;
    int i;
    /*
     * Init any data related to initrd. It‘s a nop if INITRD is
     * not selected. Once that done we can determine the low bound
     * of usable memory.
     */  /*   * 初始化所有和initrd相关的数据。如果没有INITRD,这将是一个空操作。一旦该操作完成,我们就可以确定可用内存的边界。  */
  // 计算需要为initrd保留的内存区域,那么剩余的内存就是可用内存    reserved_end = max(init_initrd(),
               (unsigned long) PFN_UP(__pa_symbol(&_end)));  // reserved_end = max(0x1b2f, 0x936) = 0x1b2f  /*
   * Redo reserved_end because there is no need to reserve so much memory(about 20MB).
  */
  reserved_end = (unsigned long) PFN_UP(__pa_symbol(&_end));  // reserved_end = 0x936 = 2358(PFN)  /*   * 上述步骤中得 PFN_UP(__pa_symbol(&end)) 不太懂,没有继续深入,而且上述步骤计算reserved_end做的一些事也是不太理解!!  */

    /*
     * max_low_pfn is not a number of pages. The number of pages
     * of the system is given by ‘max_low_pfn - min_low_pfn‘.     * 系统物理页帧总数 = max_low_pfn - min_low_pfn
     */
    min_low_pfn = ~0UL;
    max_low_pfn = 0;

    /*
     * Find the highest page frame number we have available.
     */  /*   * 寻找最大可用PFN。   * 代码很简单,记录下我们的打印,助于分析:    * memory: 0e000000 @ 00000000 (usable)    * memory:10000000 @ 30000000 (usable)   * start = 0, end = 53744    * start = 196608, end = 262144    * mapstart = 2358  */
    for (i = 0; i < boot_mem_map.nr_map; i++) {
        unsigned long start, end;

        if (boot_mem_map.map[i].type != BOOT_MEM_RAM)
            continue;

        start = PFN_UP(boot_mem_map.map[i].addr);
        end = PFN_DOWN(boot_mem_map.map[i].addr
                + boot_mem_map.map[i].size);
        if (end > max_low_pfn)
            max_low_pfn = end;
        if (start < min_low_pfn)
            min_low_pfn = start;
        if (end <= reserved_end)
            continue;
        if (start >= mapstart)
            continue;
        mapstart = max(reserved_end, start);
    }

    if (min_low_pfn >= max_low_pfn)
        panic("Incorrect memory mapping !!!");
    if (min_low_pfn > ARCH_PFN_OFFSET) {
        pr_info("Wasting %lu bytes for tracking %lu unused pages\n",
            (min_low_pfn - ARCH_PFN_OFFSET) * sizeof(struct page),
            min_low_pfn - ARCH_PFN_OFFSET);
    } else if (min_low_pfn < ARCH_PFN_OFFSET) {
        pr_info("%lu free pages won‘t be used\n",
            ARCH_PFN_OFFSET - min_low_pfn);
    }
    min_low_pfn = ARCH_PFN_OFFSET;  // 就是0

    /*
     * Determine low and high memory ranges
     */  /*    HIGHMEM_START:定义在kernel/arch/include/asm/mach-generic/spaces.h    对于32位系统, #define HIGHMEM_START   _AC(0x20000000, UL)  【就是512M,从512M开始的物理地址认为是高端内存】    经过下边的计算:highstart_pfn = PFN_DOWN(HIGHMEM_START) = 131072, hightend_pfd = 262144             min_low_pfn = 0, max_low_pfn = 131072  */
    max_pfn = max_low_pfn;
    if (max_low_pfn > PFN_DOWN(HIGHMEM_START)) {
#ifdef CONFIG_HIGHMEM
        highstart_pfn = PFN_DOWN(HIGHMEM_START);
        highend_pfn = max_low_pfn;
#endif
        max_low_pfn = PFN_DOWN(HIGHMEM_START);
    }

    /*
     * Initialize the boot-time allocator with low memory only.     * 在系统启动过程中,内存管理尚未初始化,但内核需要分配内存 以创建各种数据结构,bootmem分配器用于在启动阶段早起的内存分配。    * 所以,init_bootmem_node计算 bootmem allocator 所需内存大小,该部分内存是作为 reserved memory。
     */
    bootmap_size = init_bootmem_node(NODE_DATA(0), mapstart,
                     min_low_pfn, max_low_pfn);

    for (i = 0; i < boot_mem_map.nr_map; i++) {
        unsigned long start, end;

        start = PFN_UP(boot_mem_map.map[i].addr);
        end = PFN_DOWN(boot_mem_map.map[i].addr
                + boot_mem_map.map[i].size);

        if (start <= min_low_pfn)
            start = min_low_pfn;
        if (start >= end)
            continue;

#ifndef CONFIG_HIGHMEM
        if (end > max_low_pfn)
            end = max_low_pfn;

        /*
         * ... finally, is the area going away?
         */
        if (end <= start)
            continue;
#endif
     // start 和 end 没有发生改变:       // start = 0, end = 57344    // start = 196608, end = 262144
        add_active_range(0, start, end);  // 记录物理内存的PFN信息
    }

    /*
     * Register fully available low RAM pages with the bootmem allocator.    * 注册所有的可用低端内存给bootmem allocator【bootmem allocator可以操作低端内存】
     */
    for (i = 0; i < boot_mem_map.nr_map; i++) {
        unsigned long start, end, size;

        /*
         * Reserve usable memory.
         */
        if (boot_mem_map.map[i].type != BOOT_MEM_RAM)
            continue;

        start = PFN_UP(boot_mem_map.map[i].addr);
        end   = PFN_DOWN(boot_mem_map.map[i].addr
                    + boot_mem_map.map[i].size);
        /*
         * We are rounding up the start address of usable memory
         * and at the end of the usable range downwards.
         */
        if (start >= max_low_pfn)
            continue;
        if (start < reserved_end)
            start = reserved_end;
        if (end > max_low_pfn)
            end = max_low_pfn;

        /*
         * ... finally, is the area going away?
         */
        if (end <= start)
            continue;

     /* start = 2358, end = 53744 */
        size = end - start;

        /* Register lowmem ranges 【完成注册】*/
        free_bootmem(PFN_PHYS(start), size << PAGE_SHIFT);
        memory_present(0, start, end);
    }

    /*
     * Reserve the bootmap memory.【保留bootmap memory,mapstart = 2358, PFN_PHYS(mapstart) = 0x936000, bootmap_size = 16384Byte】
     */
    reserve_bootmem(PFN_PHYS(mapstart), bootmap_size, BOOTMEM_DEFAULT);

    /*
     * Reserve initrd memory if needed.【为initrd保留内存】 Initial ramdisk at:0x81a00000 (1238816 bytes)
     */
    finalize_initrd();
}

2. init_initrd

  在 kernel/init/do_mounts_initrd.c中定义了变量:

unsigned long initrd_start, initrd_end;

  在arch_mem_init —> parse_early_param 中,不仅调用了early_parse_mem函数,同时也调用了:rd_start_early和rd_size_early。

  这两个函数就是解析 command line 中指定的 initrd 的内存信息,代码我就贴了,调用这两个函数的结果就是:

initrd_start = 0x81a00000
initrd_end  = 0x81b2e720
/*
 * command line : console=ttyS3,115200n8 [email protected] [email protected] ip=off root=/dev/ram0 rw rdinit=/init rd_start=0x81A00000 rd_size=0x0012E720
*/

  此时, arch_mem_init—>bootmem_init—>init_initrd:

/* it returns the next free pfn after initrd */
static unsigned long __init init_initrd(void)
{
    unsigned long end;

    /*
     * Board specific code or command line parser should have
     * already set up initrd_start and initrd_end. In these cases
     * perfom sanity checks and use them if all looks good.
     */
    if (!initrd_start || initrd_end <= initrd_start)
        goto disable;

    if (initrd_start & ~PAGE_MASK) {
        pr_err("initrd start must be page aligned\n");
        goto disable;
    }
    if (initrd_start < PAGE_OFFSET) {
        pr_err("initrd start < PAGE_OFFSET\n");
        goto disable;
    }

    /*
     * Sanitize initrd addresses. For example firmware
     * can‘t guess if they need to pass them through
     * 64-bits values if the kernel has been built in pure
     * 32-bit. We need also to switch from KSEG0 to XKPHYS
     * addresses now, so the code can now safely use __pa().
     */
    end = __pa(initrd_end);
    initrd_end = (unsigned long)__va(end);
    initrd_start = (unsigned long)__va(__pa(initrd_start));

    ROOT_DEV = Root_RAM0;
    return PFN_UP(end);
disable:
    initrd_start = 0;
    initrd_end = 0;
    return 0;
}
时间: 2024-08-10 09:47:32

内存管理初始化源码2:setup_arch的相关文章

内存管理初始化源码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

内存管理初始化源码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_

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

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

内存管理初始化源码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

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