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. IDEA搭建Maven Web(SSM)项目(一)——创建项目
  2. A、B、C、D四个字母,能组成多少个互不相同且无重复三位组合
  3. 但凡网络工程师会这个技能,也不至于天天抱怨工资低
  4. 关于ubuntu下无线网卡经常连不上网络的问题
  5. 记一次查深圳磨房百公里徒步照片历程
  6. OSPF区域类型及详解
  7. 不能是underfined.xxx
  8. 计算机网络中速率(date rate)和带宽的区别
  9. for循环输出菱形星星
  10. 微信小程序自定义导航栏(带汉堡包菜单)
  11. 七大步教你征服丈母娘
  12. springCloud——ribbon和zuul
  13. Kitty用HTML和css咋做,使用 CSS3 绘制 Hello Kitty
  14. 互融云供应链金融系统4.0 来了!系统又增哪些亮点?
  15. telnet 使用教程(新手篇)及问题集锦
  16. 电脑添加了无线之后要怎么连接服务器,电脑如何连接打印机,无线打印机怎么连接无线路由器...
  17. python怎么实现检验_Python数据正态性检验实现过程
  18. 关于出现Table 'mysql.mysql' doesn't exist的解决办法
  19. 功率预测发展趋势之中长期预报
  20. 关于安卓苹果手机安装证书抓https的关键步骤

热门文章

  1. 2022数据血缘关系详解
  2. python语法糖是什么_Python语法糖Syntactic Sugar
  3. scipy.stats.norm函数
  4. 程序算法艺术与实践:基础知识之函数的渐近的界
  5. 简析一下程序化交易系统在期货交易中的作用
  6. 删除Office Word (Excel)中Recent Document最近文档中本地和online打开文件路径已经不存在的文件记录
  7. 前端提高篇(八十六):jQuery的class属性操作addClass()与removeClass()、hasClass()、toggleClass()
  8. Singularity的安装
  9. Hystrix vs Sentinel
  10. Eclipse使用时遇到一直在Validating中的解决办法