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_HIGHMEMhighstart_pfn = PFN_DOWN(HIGHMEM_START);highend_pfn = max_low_pfn;
#endifmax_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_HIGHMEMif (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 = 262144add_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 mem=224M@0x0 mem=256M@0x30000000 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;
}

转载于:https://www.cnblogs.com/ronnydm/p/5889834.html

内存管理初始化源码2:setup_arch相关推荐

  1. Linux创建页表内存代码,Linux内存管理的源码实现

    399 void mem_init(l start_mem, l end_mem) 400 { 401 int i; 402 403 high_memory = end_mem; 404 for (i ...

  2. linux 内存管理slab源码,Linux内核源代码情景分析-内存管理之slab-回收

    图 1 我们看到空闲slab块占用的若干页面,不会自己释放:我们是通过kmem_cache_reap和kmem_cache_shrink来回收的.他们的区别是: 1.我们先看kmem_cache_sh ...

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

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

  4. Quartz的Scheduler初始化源码分析

    2019独角兽企业重金招聘Python工程师标准>>> Quartz的使用:http://donald-draper.iteye.com/blog/2321886  Quartz的S ...

  5. SpringMVC源码探究软件六合网站制作(一)----初始化源码

    随着软件 , 开发技术的持续发展,框架技术层出不穷.还是那句话,任何框架技术都是对基础技术的封装.所以,真正要学好用好一个框架,研究其源码都是最直接最有效的途径. 随着Spring技术体系的强势发展, ...

  6. Spring IoC容器初始化源码(1)—容器初始化入口以及setConfigLocations设置容器配置信息【一万字】

      基于最新Spring 5.x,对于基于XML的Spring IoC容器初始化过程中的setConfigLocations设置容器配置信息方法的源码进行了详细分析,最后给出了比较详细的方法调用时序图 ...

  7. spring4.1.8初始化源码学习三部曲之三:AbstractApplicationContext.refresh方法

    本章是<spring4.1.8初始化源码学习三部曲>系列的终篇,重点是学习AbstractApplicationContext类的refresh()方法: 原文地址:https://blo ...

  8. 阿加内存管理 初始化(八) 至kswapd_init

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

  9. 流量卡物联网卡管理平台源码|PHP管理系统源码

    简介: 市面流量卡物联网卡管理平台源码|PHP管理系统源码: 市面上很火的流量卡项目,可以用这套平台系统来管理,实现自定义套餐名以及流量卡运营,充值收款. 即时返佣功能都有,内附搭建教程.自行研究! ...

最新文章

  1. 南阳汉诺塔 一 java_南阳明清移民记.pdf
  2. cookie、 sessionStorage 、localStorage之间的区别和使用
  3. Python---json模块
  4. WCF服务实现客户端Cookie共享,表单验证的解决方案
  5. mvn deploy 推送到私有仓库,注意当前日期
  6. Drawable之color示例
  7. 从最大似然再看线性回归
  8. Lambda 表达式(C# 编程指南)
  9. 机器人机构学的数学基础——绪论
  10. PSP游戏开放环境的建立
  11. word2vec 的个人理解
  12. 1906: 鹊桥相会
  13. 细读《深入理解 Android 内核设计思想》(三)Binder 机制 [上]
  14. redis集群伸缩【转】
  15. 使用exe4j打包Java桌面程序为exe
  16. 算法---程序的灵魂,没错就是灵魂!
  17. 北方互动:APP开发的具体流程
  18. 聚合支付行业术语,你get到了吗?
  19. 网易雷火 2019 春季人工智能工程师实习生笔试题
  20. 货币转换 I----Python

热门文章

  1. Carla学习(六) 创建一个带有各种传感器的车辆
  2. 数据处理-scipy.stats.norm函数,归一化的作用
  3. c语言norm函数的作用,带有示例的C ++中的norm()函数
  4. 【Revit二次开发】应用程序和文档
  5. aix 修改服务器时间,AIX修改系统时间
  6. 初来乍到,我手写我心
  7. 美尼尔综合征的症状是什么?
  8. 软件定义与分类1.1
  9. 欧姆龙CP1H移位指令使用
  10. 君临天下用计算机怎么弹,君临天下!电脑版教程和按键设置