第一阶段是个时间点 : 基本堆栈建立完成
第二阶段是个时间点 : bootmem 完成建立第二阶段到 第三阶段 是个过程, 该过程中  基本堆栈管理 消亡, bootmem 完成建立 

详细过程解读

迈向第二阶段,按最终目标来看,只需要关注两点内容1. 做了哪些内存映射2. bootmem 怎么提供的接口
但是在实现过程(linux-3.0.1 arm32)中,分为了更多步骤---------------------------------------meminfo1. meminfo 填充的过程2. meminfo 检查的过程---------------------------------------memblock 3. memblock 填充的过程将meminfo中的内容dump到memblock4. 内存映射的过程(清0页表,设置页表,生成零页)清0页表映射low mem0页devicemapskmap---------------------------------------bootmem 5. bootmem 初始化的过程(激活启动时的内存分配器)将bootmem挂到全局变量上将memblock中的内容dump到bootmem该释放的释放,该保留的保留6. bootmem 接口(申请,释放)调用过程其实整个流程归结为三点1. 分析 bootloader 传过来的内存数据.用memblock管理2. 根据 memblock 管理的内存 ,建立映射3. 初始化bootmem ,使 bootmem 能够提供 申请内存 释放内存 的接口
实现分为 6个过程,其中 4 和 5 过程最重要

1. meminfo 填充的过程

在启动过程中,kernel 不会探测硬件有多少内存,uboot(或其他bootloader)也不会探测硬件有多少内存内存信息是写死在uboot(或其他bootloader)配置中的,然后经uboot通过TAG传给kernelkernel 在解析TAG后,将内存信息放置到 struct meminfo meminfo 结构体变量中.--------------------------------------------------------------------------------------------
u-boot-----------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------uboot 相关的内存tag :ATAG_INITRD2ATAG_CMDLINE ATAG_MEM-------------------CODE在 uboot1.1.6/lib_arm/armlinux.c 中的 do_bootm_linux 中 bd_t *bd = gd->bd;char *commandline = getenv ("bootargs");setup_start_tag (bd);params = (struct tag *) bd->bi_boot_params;setup_serial_tag (&params);                                                                                              setup_revision_tag (&params);                                                                                 setup_memory_tags (bd);// include/configs/smdk6410.h 中 #define CONFIG_NR_DRAM_BANKS    1 for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {                                 params->hdr.tag = ATAG_MEM;                                              params->hdr.size = tag_size (tag_mem32);                                 // ./board/samsung/smdk6410/smdk6410.c 中 dram_init// gd->bd->bi_dram[0].start = PHYS_SDRAM_1;// gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;// include/configs/smdk6410.h 中// #define PHYS_SDRAM_1        MEMORY_BASE_ADDRESS// #define MEMORY_BASE_ADDRESS 0x50000000// #define PHYS_SDRAM_1_SIZE   0x08000000  或 #define PHYS_SDRAM_1_SIZE   0x10000000params->u.mem.start = bd->bi_dram[i].start;                              params->u.mem.size = bd->bi_dram[i].size;                                params = tag_next (params);                                              }setup_commandline_tag (bd, commandline);// include/configs/smdk6410.h 中// #define CONFIG_BOOTARGS     "root=/dev/mtdblock2 rootfstype=yaffs2 init=/linuxrc console=ttySAC0,115200"// common/env_common.c 中// uchar default_environment[] = { "bootargs=" CONFIG_BOOTARGS         "\0"// lib_arm/armlinux.c 中// char *commandline = getenv ("bootargs");// setup_commandline_tag (bd, commandline);// strcpy (params->u.cmdline.cmdline, p); // p 为 commandlineif (initrd_start && initrd_end)                                              setup_initrd_tag (bd, initrd_start, initrd_end);                         setup_videolfb_tag ((gd_t *) gd);                                            setup_end_tag (bd);                                                             theKernel (0, bd->bi_arch_number, bd->bi_boot_params);--------------------------------------------------------------------------------------------
kernel------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------每个物理连续的内存区域被保存为meminfo中的一个元素也就是说在Linux使用中,整块物理内存可能是不连续的,可能其中某一中间区域是被其他cpu给使用掉了。uboot 相关的内存tag :ATAG_INITRD2(在解析cmdline时会被覆盖)ATAG_CMDLINEATAG_MEM(在解析cmdline时会被覆盖)-------------------CODE ATAG_MEMstart_kernel -> setup_arch -> setup_machine_tags -> parse_tags -> __tagtable(ATAG_MEM, parse_tag_mem32)parse_tag_mem32 -> arm_add_memory -> 赋值( meminfo.nr_banks membank.bank[x].start membank.bank[x].size membank.bank[x].highmem )__tagtable(ATAG_MEM, parse_tag_mem32);当parse_targs找到以ATAG_MEM标识的tag后,调用parse_tag_mem32,把在uboot中填充到tag里的内存起始地址和大小填充到全局变量meminfo数组中。-------------------CODE ATAG_INITRD2// 注意 ,该项不是对 meminfo 的修改,而是对 memblock 的修改start_kernel -> setup_arch -> setup_machine_tags -> parse_tags -> __tagtable(ATAG_INITRD2, parse_tag_initrd2)__tagtable(ATAG_INITRD2, parse_tag_initrd2);parse_tag_initrd2phys_initrd_start = tag->u.initrd.start;phys_initrd_size = tag->u.initrd.size;start_kernel -> setup_arch -> arm_memblock_init-> memblock_reserve(phys_initrd_start, phys_initrd_size);-------------------CODE ATAG_CMDLINE1. __tagtable(ATAG_CMDLINE, parse_tag_cmdline); 中 strlcpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE); 2. setup_machine_tags 中 char *from = default_command_line;strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);3. parse_early_param 中    static __initdata char tmp_cmdline[COMMAND_LINE_SIZE];strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);parse_early_options(tmp_cmdline);/*start_kernel -> setup_arch -> parse_early_param -> parse_early_options(tmp_cmdline)*/  -> parse_args -> parse_one -> do_early_param -> 解析 __setup_start 和 __setup_end 之间的 结构体 (用 __setup  和  early_param 标识)early_param("mem", early_mem);do_early_param -> early_mem -> arm_add_memory -> 赋值( meminfo.nr_banks membank.bank[x].start membank.bank[x].size membank.bank[x].highmem )在内核中解析命令行中以"mem="开头的命令行,找到此命令行后,调用early_mem函数解析命令行。格式如果内核需要管理几段不同的内存,可以在uboot的bootarg环境变量中分别指定对应的内存段的起始地址和长度,如下所示:mem=72M@0xe2000000 mem=128M@0xe8000000表示内核需要管理两段不连续的内存,第一段起始地址为0xe2000000,大小为72M另外一段起始地址为0xe8000000,大小为128M内核通过early_mem函数分别把这两段内存写入到meminfo的两个bank中。early_param("initrd", early_initrd);do_early_param -> early_initrd -> phys_initrd_start = start;phys_initrd_size = size;功能:设置phys_initrd_start 和 phys_initrd_size 的值格式 为 initrd=0xd0000000,38711808 或者 initrd=0x00800000,16M// 注意 ,该项不是对 meminfo 的修改,而是对 memblock 的修改
  • ok6410-A 实际情况
parse_tag_mem32 进了一次做了一次 arm_add_memory . 其中 mem.start:0X50000000,mem.size:0X10000000
early_initrd 没进入
parse_tag_initrd2 没进入

2. meminfo 检查的过程

                 start_kernel -> setup_arch -> sanity_check_meminfosanity_check_meminfo// 1.确定本设备物理内存的各个node各个bank中到底有没有高端内存,根据是否存在高端内存决定每个bank的highmem成员值// 2.将那些部分重叠highmem区域的内存区域分开,大大简化了以后的工作// 3.赋值 lowmem_limit,所有region中非高端内存的end的最大值,并 将此值赋值 memblock.current_limit// 4.检查cache 是否支持高端内存(如果cache_is_vipt_aliasing,则不支持高端内存),计算 普通内存和可用的高端内存 数量 为 meminfo.nr_banks.// 判断有高端内存(highmem)的依据 : __va(bank->start) >= vmalloc_min || __va(bank->start) < (void *)PAGE_OFFSET// vmalloc_min = VMALLOC_END - SZ_128M = 0xF4000000UL - 0x08000000 = EC00 0000// PAGE_OFFSET = 0xC000 0000___________                     _______________//      高端内存   |     非高端内存       |     高端内存0xC000 0000                   0xEC00 0000// highmem 用于 vmalloc// 总之做以下事情  // 1. 赋值 memblock.current_limit// 2. 根据 是否 包括 highmem 对 meminfo.bank 进行 recipe , 可能会 增加 meminfo.bank的数量// 3. 赋值 meminfo.nr_banks// 4. 根据 包括 highmem  && cache 组织方式不为 VIPT aliasing , 赋值 meminfo.nr_banks 为 1struct membank {                                                                    phys_addr_t start;                                                              unsigned long size;                                                             unsigned int highmem;
};                                                                                  struct meminfo {                                                                    int nr_banks;                                                                   struct membank bank[NR_BANKS];
}; 
  • ok6410-A 实际情况
meminfo.nr_banks = 1
meminfo.bank[0].hignmem:0
meminfo.bank[0].start:0x50000000
meminfo.bank[0].size:0x10000000

3. memblock 填充的过程

         start_kernel -> setup_arch -> arm_memblock_init这个过程会将 meminfo 保存的物理内存信息 传给了一个叫struct  memblock memblock 的结构变量,后续由它来完成内存区域信息保存的责任。他会记录1.所有的内存2.所有的内存中已使用的部分1. kernel的text, code段2. initrd3. 页表arm_memblock_init(&meminfo, mdesc);// 1. 对 meminfo.bank 进行排序(根据bank[i].start对应的页帧号码排序)// 2. 对 memblock 全局变量赋值// 3. 调用 memblock_add->memblock_add_region 把 meminfo 中的内存信息 添加 到 memblock.memory 中// 4. 调用 memblock_reserve->memblock_add_region 把 以下 中的内存信息 添加 到 memblock.reserved 中// 4.1 kernel : start : __pa(_stext)  size : _end - _stext// 4.2 initrd : phys_initrd_start, phys_initrd_size// 4.3 swapper_pg_dir : __pa(swapper_pg_dir), PTRS_PER_PGD * sizeof(pgd_t)// 5. 至此,内存已经注册完了,赋值memblock.memory_size// 6. dump(展示,打印) 出 MEMBLOCK 的配置memblock所管理的实际上是物理内存,CPU要使用这些物理内存,还差一步,那就是虚拟地址,物理地址映射。stext到start_kernel过程中建立的临时页表是否依然可以沿用?不能前面建立的 映射关系 只有 3组 ,其中在 此时(arm_memblock_init执行后), 2组还有意义1. kernel 的映射2. atags 的映射除了这些部分有物理地址 <-> 虚拟地址的 映射还有大量的 物理地址(在ok6410上总共有128M或256M) 没有创建映射关系,所以需要创建剩余的映射关系struct memblock {                                                                   phys_addr_t current_limit;                                                      phys_addr_t memory_size;    /* Updated by memblock_analyze() */                 struct memblock_type memory;                                                 struct memblock_type reserved;
};struct memblock_type {                                                              unsigned long cnt;  /* number of regions */                                     unsigned long max;  /* size of the allocated array */                           struct memblock_region *regions;
};struct memblock_region {                                                            phys_addr_t base;                                                               phys_addr_t size;
};
  • ok6410-A 实际情况
MEMBLOCK configuration:memory size = 0x10000000 // 表示内存的总大小memory.cnt  = 0x1 // 表示总的内存由一块连续的地址空间构成memory[0x0]    [0x00000050000000-0x0000005fffffff], 0x10000000 bytes // 第一块内存的[起始地址,结束地址],大小reserved.cnt  = 0x1 // 表示有1块连续的地址空间被保留reserved[0x0]  [0x00000050004000-0x00000050aad573], 0xaa9574 bytes // 第一块被保留的内存的[起始地址,结束地址],大小第一块保留内存 [0x00000050004000-0x00000050aad573] 由 2小块构成第一块 : start:0x50008000,size:0xaa5574,表示内核镜像所在空间,memblock_reserve(__pa(_stext), _end - _stext);第二块 : start:0x50004000,size:0x4000,表示内存页表所在空间,arm_mm_memblock_reserve()->memblock_reserve(__pa(swapper_pg_dir), PTRS_PER_PGD * sizeof(pgd_t));

4. 内存映射的过程

    start_kernel -> setup_arch -> paging_initstext到start_kernel过程中建立的临时页表不包括所有内存的映射.所以需要创建所有物理内存的映射关系paging_init1. 准备向页表空间写入的数据内核维护了一系列内存类型,不同的内存类型对应了不同的值.这些值会作为页表项的一部分写到页表项中.build_mem_type_table2. 将页表空间清0prepare_page_table// 1. (之前建立的)段表物理地址 5000 7000 - 5000 7024               ,关系: 5000 0000 - 5090 0000 : C000 0000- C090 0000// 2. 段表物理地址 5000 4000 - 5000 4000 + 5FF*4       ,关系: x         - x         : 0000 0000- C000 0000// 3. 段表物理地址 5000 4000+608*4 - 5000 4000 + 7A0*4 ,关系: x         - x         : C100 0000- F400 00003. 向页表空间写数据(创建映射关系)map_lowmem// 根据 memblock 中的每个 memory region, create_mapping// 综上,共创建 64个一级表,64*1024个二级表.// 一级表范围在 0xC000 4600 - 0xC000 4600 + 64*4 // 二级表范围在 memblock_alloc 申请出来的空间,该空间在直接映射空间.(C000 0000 - C090 0000)// 目的是能够通过 虚拟地址 访问 物理地址(5000 0000 - 5800 0000)devicemaps_init// 做 machine vectors 的 create_mapping// 创建1个一级页表,在 C010 3FF0// 创建8个二级页表// 目的是 将 虚拟地址 0xffff0000 与 物理地址建立联系// 能够通过 虚拟地址 访问 vectors_page// 但此时 该 物理地址 空间内还没填充 vectors_page// memblock 中包括 低端内存和高端内存(vmalloc)// 这个过程 做了 低端内存的映射 , 采用的是 线性映射// 也做了 高端内存的映射,采用 高端内存映射技术2:非连续内存映射kmap_init// 初始化了一级表// 申请了二级表的空间,位置在 pkmap_page_table//  pkmap_page_table 保存额 PKMAP_BASE 对应的页表项地址,在 该地址之后,还有512-1或1024-1个页表项用于内存映射// 采用 高端内存映射技术2:永久内存映射zero_page// 申请了二级表的空间,位置在 zero_page, 对应的 struct page 地址empty_zero_page // empty_zero_page is a special page that is used for  zero-initialized data and COW当一个进程第一次对一个页进行读操作时,而且该页不在内存中时,kernel应该给进程分配一个新的页帧,更安全的做法是分配一个filled with zero的页帧给进程,这样才能保证别的进程的信息不会被新的进程给读取所以linux中保留的一个这样filled with zero的页帧,叫zero page,当这种情况发生时,系统就将zero page填入页表中,并标记为不可写。当进程要对zero page进行写操作时则
copy on write 机制就会被触发。虽然过程中有创建section的可能,但是前提是虚拟地址后20位为0,因为不满足,所以就没有创建section建立页表// paging_init 做完之后 我们还不能用 alloc_bootmem 安全的进行内存的分配,因为 alloc_bootmem 要去索引 bdata_list 链表, 而 在 bootmem_init 中 才将  NODE_DATA(0) 即(contig_page_data) 插入 bdata_list 中1. 所有的内存都可以通过bootmem机制来分配和释放.2. pagging_init创建了页表, 为内核提供了一套可供内核和进程运行的虚拟运行空间TODO1. 校验是否为高1G空间都建立了页表2. 思考低3G怎么建立页表3. 思考如果小于4G怎么建立页表4. 思考如果大于4G怎么建立页表5. zero_page 和 kmap_init 是做什么用的6. devicemaps_init 是做什么用的
  • ok6410-A 实际情况
memblock.current_limit:0x60000000
build_mem_type_table,ARRAY_SIZE(mem_types):141. 一级页表映射 清0情况
// 1.Clear out all the mappings below the kernel image
prepare_page_table,pmd_off_k(0):0xc0004000
prepare_page_table,pmd_off_k(200000):0xc0004008
...
prepare_page_table,pmd_off_k(bee00000):0xc0006fb8
// 2.skip over XIP kernel
prepare_page_table,pmd_off_k(bf000000):0xc0006fc0
...
prepare_page_table,pmd_off_k(bfe00000):0xc0006ff8// 3.Clear out all the kernel space mappings, except for the first memory bank, up to the end of the vmalloc region.
prepare_page_table,pmd_off_k(d0000000):0xc0007400
...
prepare_page_table,pmd_off_k(f3e00000):0xc0007cf8// 4.address after VMALLOC_END
devicemaps_init,pmd_off_k(f4000000):0xc0007d00
devicemaps_init,pmd_off_k(f4200000):0xc0007d08
...
devicemaps_init,pmd_off_k(ffe00000):0xc0007ff8// 5. 可以看出 C000 0000 - D000 0000 的空间映射(即0xc0007000-0xc00073ff)没有清0// 因为 0xC000 0000-0xD000 0000 的空间映射 (即0xc0007000-0xc00073ff) 用于 页表 和 内核的存放了// 但只有 其中的(0xC000 4000 - C0aa d573)的空间映射(即 C000 7000 - C000 7024
) 用于 页表和内核的存放,其中的其他位置都是空闲的// 所以这个是怎么设置的???2.一级页表映射填充,二级页表映射填充  // 总共有 13 次 create_mapping,包括1次lowmem映射,1次vectors_page映射,11次 smdk6410_map_io 映射1次 lowmem 映射
map_lowmem // 低端内存相关的映射 (都是一级SECTION映射,除了map_lowmem,其他都是一级二级页表映射)md->pfn:0x50000,md->virtual:0xc0000000,md->length:0x10000000,md->type:0x9map.pfn:0x50000map.virtual:0xc0000000map.length:0x10000000map.type:0x91次 vectors_page 映射  // vector 初始化 请查看 early_trap_init
devicemaps_init // vectors_page相关的映射md->pfn:0x5ffff,md->virtual:0xffff0000,md->length:0x1000,md->type:0x8map.pfn:0x5ffffmap.virtual:0xffff0000map.length:0x1000map.type:0x811 个    paging_init-> devicemaps_init -> mdesc->map_io(smdk6410_map_io) 做的映射smdk6410_map_io -> iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc)); (10次)md->pfn:0x7e00f,md->virtual:0xf4100000,md->length:0x1000,md->type:0x0md->pfn:0x70000,md->virtual:0xf4200000,md->length:0x1000,md->type:0x0md->pfn:0x7f005,md->virtual:0xf5005000,md->length:0x1000,md->type:0x0md->pfn:0x71200,md->virtual:0xf4000000,md->length:0x4000,md->type:0x0md->pfn:0x71300,md->virtual:0xf4010000,md->length:0x4000,md->type:0x0md->pfn:0x7f006,md->virtual:0xf4300000,md->length:0x4000,md->type:0x0md->pfn:0x7f008,md->virtual:0xf4500000,md->length:0x1000,md->type:0x0md->pfn:0x74108,md->virtual:0xf4600000,md->length:0x1000,md->type:0x0md->pfn:0x7e004,md->virtual:0xf4400000,md->length:0x1000,md->type:0x0md->pfn:0x7c100,md->virtual:0xf4700000,md->length:0x400,md->type:0x0s3c_iodesc 结构体.pfn = __phys_to_pfn(x) x              描述(在s3c6410数据手册中找)S3C64XX_PA_SYSCON         0x7E00F000      System ControllerS3C64XX_PA_SROM        0x70000000      SROM SFRS3C_PA_UART             0x7F005000      UARTS3C64XX_PA_VIC0         0x71200000      VIC0S3C64XX_PA_VIC1         0x71300000      VIC1S3C_PA_TIMER            0x7F006000      PWM TimerS3C64XX_PA_GPIO        0x7F008000      GPIOS3C64XX_PA_MODEM        0x74108000      Direct Host I/FS3C64XX_PA_WATCHDOG  0x7E004000      Watch-Dog TimerS3C64XX_PA_USB_HSPHY     0x7C100000      USB OTG SFRsmdk6410_map_io -> iotable_init(mach_desc, size); (1次)md->pfn:0x77100,md->virtual:0xf5100000,md->length:0x4000,md->type:0x0smdk6410_iodesc 结构体.pfn = __phys_to_pfn(x) x              描述S3C_PA_FB             0x77100000      LCD Controller页表大概范围一级段表大概在 0xc0007000 - 0xc00073ff一级页表大概在 0xc0007420 - 0xc0007fff二级页表大概在 0xcfff9000 - 0xcfffe7c03. 其他pkmap pagepkmap_page_table:0xccaf8000一级页表有填充,二级页表给出了地址0xccaf8000,但是二级 页表没有填充top_pmdpmd_off_k(0xffff0000) = 0xc0007ff8zero pageLEVEL2 : PAGE:AT:0xccaf7000,Mapping ZERO一级页表没有填充,二级页表给出了地址 0xccaf7000, 二级页表填充为0zero_page:0xccaf7000zero_page 的值 为 0xccaf7000Mapping ZERO Page in PAGE : empty_zero_page:0xc0a63ee0将zero page 纳入到 PAGE 架构中,得出 zero page 在 PAGE 架构中的 struct page 结构体变量地址(0xc0a63ee0)对应第三阶段 (没有多大意义,暂不考虑,该项无意义)Memory: 256MB = 256MB totalMemory: 194628k/194628k available, 67516k reserved, 0K highmemVirtual kernel memory layout:// 4.address after VMALLOC_ENDvector  : 0xffff0000 - 0xffff1000   (   4 kB)fixmap  : 0xfff00000 - 0xfffe0000   ( 896 kB)DMA     : 0xff600000 - 0xffe00000   (   8 MB)// 3.Clear out all the kernel space mappings, except for the first memory bank, up to the end of the vmalloc region.vmalloc : 0xd0800000 - 0xf4000000   ( 568 MB)// 没清0的空间lowmem  : 0xc0000000 - 0xd0000000   ( 256 MB).init : 0xc0008000 - 0xc0036000   ( 184 kB).text : 0xc0036000 - 0xc0800888   (7979 kB).data : 0xc0802000 - 0xc084af50   ( 292 kB).bss : 0xc084af74 - 0xc0aad574   (2442 kB)pkmap   : 0xbfe00000 - 0xc0000000   (   2 MB)// 1.Clear out all the mappings below the kernel image// 2.skip over XIP kernelmodules : 0xbf000000 - 0xbfe00000   (  14 MB)

5. bootmem 初始化的过程

  • struct pglist_data 中的 bootmem 相关变量 和 buddy 相关变量
--------------------------------------------------------------------------------------------------------------struct pglist_data 中的 bootmem 相关变量 和 buddy 相关变量按道理来讲 bootmem 不应该 和 buddy 缠到一块去, 但是 内核 就是这么做了结构体上的缠绕 : 内核在  struct pglist_data 中 放入 了 bootmem 相关变量 和 buddy 相关变量 初始化时的缠绕 : 内核在刚初始化玩 bootmem(相关变量) 就去初始化 buddy(相关变量)了接下来 写的是   struct pglist_data 的成员
--------------------------------------------------------------------------------------------------------------// 总的来说 bootmem 是通过 操作  struct pglist_data 结构体 来实现 bootmem机制的// 在初始化过程中,主要的工作就是 初始化 struct pglist_data contig_page_data 变量// contig_page_data  中的 bdata node相关 zone相关 成员// 初始化 bdata 成员 的时候 做了个动作 ,list_add_tail(&bdata->list, iter); // iter 为 bdata_list 链表中的节点地址 // static struct list_head bdata_list __initdata = LIST_HEAD_INIT(bdata_list);// bdata->node_bootmem_map 是 位图数据 起始地址// struct pglist_data 中所有的成员都要初始化// 1. node_zones// 2. bdata// 3. node_mem_map
  • bootmem 的建立过程
--------------------------------------------------------------------------------------------------------------bootmem 的建立过程
--------------------------------------------------------------------------------------------------------------
start_kernel -> setup_arch -> paging_init -> bootmem_init-> arm_bootmem_init/** Allocate the bootmem bitmap page.  This must be in a region* of memory which has already been mapped.*/// 计算出 位图数据大小 对应的 page 数目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));/** Initialise the bootmem allocator, handing the* memory banks over to bootmem.*/// 当前 MAX_NUMNODES 为1 , node_set_online(0) 什么都不做node_set_online(0);// pgdat = &contig_page_data . contig_page_data 为 全局变量//struct pglist_data __refdata contig_page_data = {//    .bdata = &bootmem_node_data[0]//};pgdat = NODE_DATA(0);// pgdat 为 &contig_page_data// __phys_to_pfn(bitmap) 为 位图数据 对应的 pfn// start_pfn 为 位图数据管理的内存的 起始 pfn// end_pfn   为 位图数据管理的内存的 最后一个 pfn//// init_bootmem_node 要做的事 是 init_bootmem_node -> init_bootmem_core// A.初始化 contig_page_data->bdata 中的 成员变量//      A.1. node_bootmem_map   // bootmem 位图数据 的起始物理地址//       A.2. node_min_pfn       // bootmem 管理的 低端内存的起点//        A.3. node_low_pfn           // bootmem 管理的 低端内存的终点//        A.4. list               // bootmem 可能有多个不连续的内存块,这些不连续的内存块需要用 链表链起来// B初始化位图数据中的每一位 为 1,表示内存不可用init_bootmem_node(pgdat, __phys_to_pfn(bitmap), start_pfn, end_pfn);// 解析 memblock 中的 memory 成员变量,得到 未使用的内存信息// 然后 调用 free_bootmem -> mark_bootmem(start, end, 0, 0) -> __free -> test_and_clear_bit , 将位图数据的位 置0,表示内存可用/* Free the lowmem regions from memblock into bootmem. */for_each_memblock(memory, reg) {unsigned long start = memblock_region_memory_base_pfn(reg);unsigned long end = memblock_region_memory_end_pfn(reg);if (end >= end_pfn)end = end_pfn;if (start >= end)break;free_bootmem(__pfn_to_phys(start), (end - start) << PAGE_SHIFT);}// 解析 memblock 中的 reserved 成员变量,得到 已使用的内存信息// 然后 reserve_bootmem mark_bootmem(start, end, 1, 0) -> __reserve -> test_and_set_bit , 将位图数据的位 置1,表示内存不可用/* Reserve the lowmem memblock reserved regions in bootmem. */for_each_memblock(reserved, reg) {unsigned long start = memblock_region_reserved_base_pfn(reg);unsigned long end = memblock_region_reserved_end_pfn(reg);if (end >= end_pfn)end = end_pfn;if (start >= end)break;reserve_bootmem(__pfn_to_phys(start),(end - start) << PAGE_SHIFT, BOOTMEM_DEFAULT);}
--------------------------------------------------------------------------------------------------------------至此,bootmem 已经建立完成.
--------------------------------------------------------------------------------------------------------------
  • ok6410-A 实际情况
// arm_bootmem_free(不包括) 之前 的 bootmem_init
min of mem :0x50000,max of low mem:0x60000,max of high mem:0x60000
需要两个 bootmem manage 0x2 pages(大小 0x00002000,初始化为0xff) 来 存储 0x1000 0000大小 的内存空间
用 memblock_alloc_base 申请出 用于 bootmem 位图 的 空间 bootmem bitmap addr : 0x5caf5000(物理地址) , 虚拟地址(0xccaf5000)
data->node_min_pfn:0x00050000, bdata->node_low_pfn;0x00060000将 start:0x50000000,size;0x10000000 , end = 6000 0000  , free    地址 注册到 bootmem (memblock初始化完成时就有的)
将 start:0x50004000,size;0x008ca000 , end = 508C E000  , reserve 地址 注册到 bootmem(memblock初始化完成时就有的)
将 start:0x5caf5000,size;0x0350b000 , end = 6000 0000  , reserve 地址 注册到 bootmem(memblock初始化完成时到注册时,两个时间点间调用memblock_alloc 相关函数 产生的,用于)(1.存储 二级页表)(2.存储 bootmem 的 位图信息)

6. bootmem 接口(申请,释放)调用过程

             alloc_bootmemalloc_bootmem_alignalloc_bootmem_nopanicalloc_bootmem_pagesalloc_bootmem_pages_nopanicalloc_bootmem_nodealloc_bootmem_node_nopanicalloc_bootmem_pages_nodealloc_bootmem_pages_node_nopanicalloc_bootmem_lowalloc_bootmem_low_pagesalloc_bootmem_low_pages_nodefree_bootmemfree_bootmem_latefree_bootmem_nodealloc_bootmem__alloc_bootmem___alloc_bootmem       ___alloc_bootmem_nopaniclist_for_each_entry(bdata, &bdata_list, list)alloc_bootmem_corevoid *region;__reservetest_and_set_bitregion = phys_to_virtreturn region;free_bootmemmark_bootmemmark_bootmem_node__freetest_and_clear_bit

其他

  • 第二阶段的内存分配器为 bootmem ,但是 建立过程 却有 memblock 的 影子 ,为什么?
因为 memblock 要去取代 bootmem,但是实际上又没有 在一个版本之内完全取代.
memblock 的代码 和 bootmem 的代码混杂到一起了.
实际上 linux-3.0.1的 bootmem 建立的过程中 memblock 只是起到了 过渡 作用, 最后 还是过渡到了 bootmem,bootmem 建立完成后,memblock 就消失了.
在过渡阶段,memblock做了两件事1. 保存物理内存信息(之后移交给bootmem管理)2. 根据保存的物理内存信息做映射.(映射做完就是就在那里,不用移交给bootmem)其实 在 bootmem_init -> arm_bootmem_init -> memblock_alloc_base 用到了 memblock 分配器 来申请内存,此时申请的内存 用于 存储 bootmem  的位图信息.
  • 第二阶段的内存分配器为 bootmem ,但是 建立过程 却有 buddy的 影子 ,为什么?
bootmem_init -> arm_bootmem_free 该函数调用前(不包括) 是 bootmem 建立的过程
bootmem_init -> arm_bootmem_free 该函数调用后(包括) 是 bootmem消亡(在bootmem_init函数中bootmem还没有涉及到消亡,不过正在自掘坟墓了),buddy建立的过程总之, bootmem_init 这个函数1.完成了 bootmem 建立的最后一步2.开始了 buddy 的建立过程

linux内存管理 (四) 3 内存管理机制 第一阶段 迈向 第二阶段的过程相关推荐

  1. Linux学习(四)---用户管理

    文章目录 Linux学习--用户管理 一.基本介绍 二.添加用户 2.1 基本语法 2.2 实际案例 2.3 细节说明 三.给用户指定或者修改密码 四.删除用户 4.1基本语法 4.2 应用案例 五. ...

  2. Hadoop生态圈(十三)- Namenode元数据管理及各组件工作机制

    目录 前言 1. Namenode元数据管理 1.1 元数据是什么 1.2 元数据管理概述 1.2.1 内存元数据 1.2.2 磁盘元数据 1.2.2.1 fsimage内存镜像文件 1.2.2.2 ...

  3. LCD不带显存,是如何内存映射屏幕。S5PV210SoC在内存中选一段内存存放颜色数据,通过配置将LCD控制器和这一段内存连接起来,构成映射关系,LCD控制器就自动从显存中读取像素数据传给LCD驱动器

    一.LCD控制原理 S5PV210处理器中自带LCD控制器,控制LCD的显示,把 LCD 图像数据从一个位于系统内存的 video buffer 传送到一个外部的 LCD 驱动器接口. 类型: STN ...

  4. Linux实战教学笔记20:初级阶段结束,中级阶段起航

    第二十节 第一阶段结束第二阶段起航 标签(空格分隔): Linux实战教学笔记-陈思齐 一,承上 Linux实战教学笔记的基础核心能力阶段也就是第一阶段到此也就告一段落了.如果同学们能基本全都掌握,再 ...

  5. linux进程管理内存管理,Linux专业知识四:Linux系统进程管理及查看内存

    本文主讲Linux专业知识之Linux系统进程管理及查看内存的情况,以Redhat RHEL7操作系统为例. 一.进程 程序与进程:程序是静态的(文件),进程是动态的(运行的程序). 进程和线程:一个 ...

  6. Linux内核机制总结内存管理之连续内存分配器(二十七)

    文章目录 1 连续内存分配器 1.1 使用方法 1.2 技术原理 重要:本系列文章内容摘自<Linux内核深度解析>基于ARM64架构的Linux4.x内核一书,作者余华兵.系列文章主要用 ...

  7. linux 内存管理 (四) 内存与IO的交互

    该文章参考宋宝华老师的内存管理课程,详细可以去听阅码场宋老师的课程. ●  内存与I/O的交换 ●  page cache ●  free命令的详细解释 ●  read.write和mmap ●  f ...

  8. linux内存管理(十四)-内存OOM触发分析

    在内存分配路径上,当内存不足的时候会触发kswapd.或者内存规整,极端情况会触发OOM,来获取更多内存. 在内存回收失败之后,会进行OOM,OOM的入口是__alloc_pages_may_oom, ...

  9. linux物理内存虚拟内存一致,Liunx内存管理的调用和实现

    下面我们探讨一下关于内存管理的系统调用方式.事实上,POSIX 并没有给内存管理指定任何的系统调用.然而,Linux 却有自己的内存系统调用,主要系统调用如下 系统调用描述s = brk(addr)改 ...

最新文章

  1. linux 单引号,双引号,反引号的小总结。
  2. 树莓派搭建Django服务器通过远程访问
  3. java表达式由什么组成_必知必会之Lambda表达式
  4. AUTOSAR从入门到精通100讲(十)-DoIP协议介绍
  5. 生成的数据库脚本没有注释?
  6. 19秋学期计算机网络基础在线作业,南开19秋学期(1709、1803、1809、1903、1909)《计算机网络基础》在线作业资料答案3...
  7. layui循环遍历数据_layui.laytpl渲染模板,遍历、输出、判断
  8. NetDevOps常用数据库python实战-influxDB_grafana
  9. 阶段1 语言基础+高级_1-3-Java语言高级_06-File类与IO流_01 File类_7_File类创建删除功能的方法...
  10. TensorFlow优化模型之正则化
  11. .EXE病毒解决(U盘中文件夹不见,显示原文件夹后缀.exe)
  12. 在Android APP内部实现一个Http Server——NanoHttpd 简单剖析
  13. 商品秒杀系统设计思路
  14. Prisma 与瘦后端
  15. UIPresentationController
  16. linux内核损坏的原因,Linux内核报错“General protection fault”原因
  17. Veeam中的几种备份方式
  18. 手把手教你10分钟部署php项目
  19. gis根据行政区计算栅格数据计算_ARCGIS栅格计算器
  20. IBM SPSS Statistics编辑数据的基本操作

热门文章

  1. 限流-RateLimiter
  2. Spring认证指南:了解如何在 GemFire 中缓存数据
  3. Android Glide缓存策略
  4. 福布斯:加密货币正准备从根本上改变金融
  5. MUI页面及遇到的bug
  6. jxls导出excel,使用sum函数无法求和的问题
  7. Java 使用jacob打印word文档
  8. 无人小飞机微信表情雨怎么触发?使命召唤,放空投弹,
  9. HTML+CSS+JS+Jquery面试题
  10. iOS 人机界面指南