Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2150439
  • 博文数量: 438
  • 博客积分: 3871
  • 博客等级: 中校
  • 技术积分: 6075
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-10 00:11
个人简介

邮箱: wangcong02345@163.com

文章分类

全部博文(438)

文章存档

2017年(15)

2016年(119)

2015年(91)

2014年(62)

2013年(56)

2012年(79)

2011年(16)

分类: LINUX

2016-11-07 17:47:44

一. 说明
1.1 内存参数的获取
a. 实模式下memory参数的获取
linux-2.4.18/arch/i386/boot/setup.S -->当时处于实模式下把E820的memory_map的结果放在了0x2D0处
  1. linux-2.4.18/include/asm-i386/e820.h
  2. #define E820MAP 0x2d0 /* our map */
  3. #define E820NR 0x1e8 /* # entries in E820MAP */
这个是在Documentation/i386/zero-page.txt中规定的

b. 保护模式下memory参数的转移
arch/i386/kernel/head.S中把参数由0x90000copy到了empty_zero_page(c0104000)

E820的map如上图所示
1.2 setup_arch中建立了boot_mem的管理,如下图所示:
1.3 页表初始化的步骤,都是在setup_arch中
setup_arch
{
    a.合并e820的map, 通过e820的map中的start与offset计算出可用的最大内存,这个值略小于物理内存
    如果最大内存大于896M,则highstart_pfn=896M页帧,highend_pfn=最大可用物理内存的页帧
    如果最大物理内存小于896M,则highstart_pfn = highend_pfn=最大可用物理内存的页帧

    b.调用init_bootmem在kernel的end处建立一个内存页帧的位图来管理内存
    contig_page_data->bdata->node_bootmem_map,里面的全部内容都设为0xFF,代表不可用

    c. 扫描e820的map,将所有标志为内存的部分在bootm内存管理区中标志为0,代表可用
       经此操作  除管理1M的部分会留下一些0xFF,其余的bootm内存管理区全为0

    d. 调用reserve_bootmem(HIGH_MEMORY, size);将内核及bootm内存管理区在bootm内存管理区设为不可用0xFF

    到此bootm页表管理己初始化完成,以后可以自由的使用bootm来分配及删除内存页表了。    
}
以上b完成后bootm内存管理区的内容如下所示
  1. (gdb) x /200bx 0x381000
  2. 0x381000:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
  3. 0x381008:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
  4. 0x381010:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
  5. ....
  6. 0x388000:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
以上c完成后bootm内存管理区的内容如下所示
  1. (gdb) x /200bx 0x381000
  2. 0x381000:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
  3. 0x381008:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
  4. 0x381010:    0x00    0x00    0x00    0x80    0xff    0xff    0xff    0xff
  5. 0x381018:    0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff
  6. 0x381020:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
  7. ....
  8. 0x388000:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
以上d完成后bootm内存管理区的内容如下所示:
  1. (gdb) x /500bx 0x381000
  2. 0x381000:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
  3. 0x381008:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
  4. 0x381010:    0x00    0x00    0x00    0x80    0xff    0xff    0xff    0xff
  5. 0x381018:    0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff  -->这部分是[0x9FC00-1M]设为不可用
  6. 0x381020:    0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff
  7. 0x381028:    0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff
  8. 0x381030:    0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff
  9. 0x381038:    0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff
  10. 0x381040:    0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff
  11. 0x381048:    0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff
  12. 0x381050:    0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff
  13. 0x381058:    0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff
  14. 0x381060:    0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff
  15. 0x381068:    0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff  -->这部分是[1M-boom结束]设为不可用
  16. 0x381070:    0xff    0x01    0x00    0x00    0x00    0x00    0x00    0x00
  17. ....
  18. 0x388000:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00


二.代码说明
2.1 内存参数的获取
linux-2.4.18/arch/i386/kernel/setup.c
  1. void __init setup_arch(char **cmdline_p)
  2. {
  3.     unsigned long bootmap_size, low_mem_size;
  4.     unsigned long start_pfn, max_pfn, max_low_pfn;
  5.     int i;
  6. //以下是执行完setup_memory_region后的结果,qemu中设置了mem=256
  7. //(gdb) p /x e820
    $4 = {nr_map = 0x6, map = {{addr = 0x0, size = 0x9fc00, type = 0x1}, {addr = 0x9fc00, size = 0x400, type = 0x2}, {addr = 0xf0000, size = 0x10000, type = 0x2}, {addr = 0x100000, 
          size = 0xfefe000, type = 0x1}, {addr = 0xfffe000, size = 0x2000, type = 0x2}, {addr = 0xfffc0000, size = 0x40000, type = 0x2}, {addr = 0x0, size = 0x0, 
          type = 0x0} }}  -->共有6个map
    当设置mem=1024时
    (gdb) p /x e820
    $1 = {nr_map = 0x6, map = {{addr = 0x0, size = 0x9fc00, type = 0x1}, {addr = 0x9fc00, size = 0x400, type = 0x2}, {addr = 0xf0000, size = 0x10000, type = 0x2}, {addr = 0x100000, 
          size = 0x3fefe000, type = 0x1}, {addr = 0x3fffe000, size = 0x2000, type = 0x2}, {addr = 0xfffc0000, size = 0x40000, type = 0x2}, {addr = 0x0, size = 0x0, type = 0x0} }}
  8.     setup_memory_region();

  9.     init_mm.start_code = (unsigned long) &_text;
  10.     init_mm.end_code = (unsigned long) &_etext;
  11.     init_mm.end_data = (unsigned long) &_edata;
  12.     init_mm.brk = (unsigned long) &_end;

  13.     code_resource.start = virt_to_bus(&_text);
  14.     code_resource.end = virt_to_bus(&_etext)-1;
  15.     data_resource.start = virt_to_bus(&_etext);
  16.     data_resource.end = virt_to_bus(&_edata)-1;

  17.     parse_mem_cmdline(cmdline_p);

  18. #define PFN_UP(x)    (((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
  19. #define PFN_DOWN(x)    ((x) >> PAGE_SHIFT)
  20. #define PFN_PHYS(x)    ((x) << PAGE_SHIFT)

  21. /*
  22.  * Reserved space for vmalloc and iomap - defined in asm/page.h
  23.  */
  24. #define MAXMEM_PFN    PFN_DOWN(MAXMEM)
  25. #define MAX_NONPAE_PFN    (1 << 20)

  26. //在System.map中0xc044daac A _end
  27. //end的物理地址PA= 0xc044daac - 0xc000000 = 0x44daac
    //#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
    //内存管理的开始页帧start_pfn = (0x44daac+4k-1)/4k = 1102.666748047 =1102
  28.     start_pfn = PFN_UP(__pa(&_end));
  29. //解析e820的map表,获取可用的mem的最高地址,我这儿1G的内存max_pfn=0x3fffe,1G内存最大的pfn=0x40000
  30. //但最后两页0x2000不是可用mem,所以max_pfn=0x40000-2=0x3fffe
  31.     max_pfn = 0;
  32.     for (i = 0; i < e820.nr_map; i++) {
  33.         unsigned long start, end;
  34.         /* RAM? */
  35.         if (e820.map[i].type != E820_RAM)   -->跳过保留内存区,#define E820_RESERVED 2
  36.             continue;
  37.         start = PFN_UP(e820.map[i].addr);   -->将物理地址转为页帧号
  38.         end = PFN_DOWN(e820.map[i].addr + e820.map[i].size)-->将物理地址转为页帧号
  39.         if (start >= end)
  40.             continue;
  41.         if (end > max_pfn)          
  42.             max_pfn = end;      -->max_pfn可以理解为实际内存容量的页帧(根据e820map实际会减一点点)
  43.     }

  44. //获取最高地址地页帧号,大于896M的内存其最高地址的页帧号就是896M的页帧
  45.     max_low_pfn = max_pfn;
  46.     if (max_low_pfn > MAXMEM_PFN) {   
  47.         max_low_pfn = MAXMEM_PFN;    //当内存大于896M时,max_low_pfn=896M的页帧=0x38000
  48. #ifndef CONFIG_HIGHMEM
  49.         /* Maximum memory usable is what is directly addressable */
  50.         printk(KERN_WARNING "Warning only %ldMB will be used.\n", MAXMEM>>20);
  51.         if (max_pfn > MAX_NONPAE_PFN)
  52.             printk(KERN_WARNING "Use a PAE enabled kernel.\n");
  53.         else
  54.             printk(KERN_WARNING "Use a HIGHMEM enabled kernel.\n");
  55. #else /* !CONFIG_HIGHMEM */
  56. #ifndef CONFIG_X86_PAE
  57.         if (max_pfn > MAX_NONPAE_PFN) {
  58.             max_pfn = MAX_NONPAE_PFN;
  59.             printk(KERN_WARNING "Warning only 4GB will be used.\n");
  60.             printk(KERN_WARNING "Use a PAE enabled kernel.\n");
  61.         }
  62. #endif /* !CONFIG_X86_PAE */
  63. #endif /* !CONFIG_HIGHMEM */
  64.     }

  65. #ifdef CONFIG_HIGHMEM
  66.     highstart_pfn = highend_pfn = max_pfn;   -->max_pfn可以理解为实际内存容量的页帧=0x3fffe
  67.     if (max_pfn > MAXMEM_PFN) {
  68.         highstart_pfn = MAXMEM_PFN;          -->highstart_pfn=0x38000(896M的页帧)
  69.         printk(KERN_NOTICE "%ldMB HIGHMEM available.\n",
  70.             pages_to_mb(highend_pfn - highstart_pfn));
  71.     }
  72. #endif
  73.     //内存管理区的开始是内核end处,并页对齐的地址-->我这儿是0x381000
  74.     //当内存大于896M时,只用896M,内存管理区中1bit代表1页。 
  75.     //bit[0]映射[0-4k]这块内存以此类推,bit[1]映射[4-8k]这块内存以此类推,
  76.     //最后将内存管理区全置1-->我这儿是将[0x381000-0x388000]这块内存全置1
  77.     bootmap_size = init_bootmem(start_pfn, max_low_pfn);    -->bootmap_size=0x7000

  78.     //在init_bootmem中将内存管理区全置1,代表己被占用,哪些内存可以用呢?
  79.     //答案是在e802的mem_map中属性为E820_RAM的是可以用的
  80.     //下面这个for循环查找e820的mem_map找到属性为E820_RAM的内存
  81.     //将这部分内存在内存管理区中置0,代表这块内存可用
  82.     for (i = 0; i < e820.nr_map; i++) {
  83.         unsigned long curr_pfn, last_pfn, size;
  84.         if (e820.map[i].type != E820_RAM)
  85.             continue;
  86.         curr_pfn = PFN_UP(e820.map[i].addr);
  87.         if (curr_pfn >= max_low_pfn)
  88.             continue;
  89.         last_pfn = PFN_DOWN(e820.map[i].addr + e820.map[i].size);

  90.         if (last_pfn > max_low_pfn)   -->如果这个内存区结束地址大于896M
  91.             last_pfn = max_low_pfn;   -->则设置内存区结束地址为896M-->并转为页帧
  92.         if (last_pfn <= curr_pfn)
  93.             continue;

  94.         size = last_pfn - curr_pfn;
  95.         free_bootmem(PFN_PHYS(curr_pfn), PFN_PHYS(size));   -->将内存管理区中[0-896M]中可用部分置0
  96.     }
  97.     //将内核及内存管理区所在的内存 在内存管理区相应的映射地址处置1 -->表明这部分内存不可用
  98.     //内核的end=380150,页对齐=0x381000, 整个不可用部分的size=(0x381000-0x100000)+0x7000=0x288000
  99.     //不可用部分的起始地址=0x100000=1M转为页帧256, 长度0x288000转为页帧=0x288000/4096=648
  100.     //转为位图0x381000+256/8=0x20    648/8=81 [0x381000+0x20 --- +81]这个内存管理区置1
  101.     //即将[0x381020-0x0x381071]置1
  102.     reserve_bootmem(HIGH_MEMORY, (PFN_PHYS(start_pfn) bootmap_size + PAGE_SIZE-1) - (HIGH_MEMORY));

  103.     reserve_bootmem(0, PAGE_SIZE);             -->将[0-4K]设为不可用   -->内存管理区0x381000: 0x01

  104. #ifdef CONFIG_SMP
  105.     reserve_bootmem(PAGE_SIZE, PAGE_SIZE);     -->将[4K-8K]设为不可用 -->内存管理区0x381000: 0x03
  106. #endif
  107. ....                                            --->到此己经可以用bootm来进行内存分配了
  108. }
注: 关于setup_memory_region
start_kernel --> setup_arch --> setup_memory_region
    --> sanitize_e820_map  ;排序合并e820中的数据
    --> copy_e820_map       ;将e820的map数据储存在结构体e820 (它是map[E820MAX])中
当内存小于896M时, highstart_pfn=内存最大值/4K,例如256M=256×1024×1024/4096=65536=0x10000
当内存大于896M时, highstart_pfn=896M/4K=896×1024×1024/4096=1939524096/4096=0x38000

2.2 bootm内存管理区的初始化
linux-2.4.18/mm/bootmem.c
setup_arch
 -->init_bootmem_core
  1.  --> 896*1024*1024/4096=229376=0x38000
     --> 需要用byte的位图 380000/8=28672=0x7000,所以内存管理区的大小是0x7000个字节,管理内存是896M
     -->将[0x381000-0x388000]这块内存全置1
  2. //mapstart--> 是内核结束的地址(按页对齐)的页帧 ,这个就是内存的位图管理区的始地址(我这儿0x381000)
    //start -->是 0
    //end --> 是内存结束或896M的页帧
  3. static unsigned long __init init_bootmem_core (pg_data_t *pgdatunsigned long mapstart, unsigned long start, unsigned long end)
  4. {
  5.     bootmem_data_t *bdata = pgdat->bdata;
  6.     unsigned long mapsize = ((end - start)+7)/8;                    -->mem的管理是按位图来管理的,1位代表1页

  7.     pgdat->node_next = pgdat_list;
  8.     pgdat_list = pgdat;

  9.     mapsize = (mapsize + (sizeof(long) - 1UL)) & ~(sizeof(long) - 1UL);
  10.     bdata->node_bootmem_map = phys_to_virt(mapstart << PAGE_SHIFT);  //将位图管理区的起始地址(0xc0381000)转为虚地址
  11.     bdata->node_boot_start = (start << PAGE_SHIFT);
  12.     bdata->node_low_pfn = end;

  13.     /*
  14.      * Initially all pages are reserved - setup_arch() has to
  15.      * register free RAM areas explicitly.
  16.      */
  17.     memset(bdata->node_bootmem_map, 0xff, mapsize);             -->将位图所有位置1,表明己被占用

  18.     return mapsize;
  19. }
2.3 bootm内存的分配
其中goal的作用是:从哪开始分配内存
例如:goal=0x0则从0开始分配内存,当然能不能从0分配到内存还得看内存管理区位图是否是0
当goal=0x1000000时,则从16M开始分配内存,前面即使有空闲内存也不用了
2.3.1 goal=0
在mm/bootmem.c中
setup_arch-->smp_alloc_memory-->alloc_bootmem_low_pages-->__alloc_bootmem_node
-->__alloc_bootmem_core
  1. #define alloc_bootmem_low_pages(x) \
  2.     __alloc_bootmem((x), PAGE_SIZE, 0)
goal=0
  1. static void * __init __alloc_bootmem_core (bootmem_data_t *bdata,
  2.     unsigned long size, unsigned long align, unsigned long goal)
  3. {
  4.     unsigned long i, start = 0;
  5.     void *ret;
  6.     unsigned long offset, remaining_size;
  7.     unsigned long areasize, preferred, incr;
  8.     unsigned long eidx = bdata->node_low_pfn - (bdata->node_boot_start >>PAGE_SHIFT);   //eidx=0x38000=896M的页帧

  9.     if (!size) BUG();

  10.     if (align & (align-1))
  11.         BUG();

  12.     offset = 0;
  13.     if (align &(bdata->node_boot_start & (align - 1UL)) != 0
  14.         offset = (align - (bdata->node_boot_start & (align - 1UL)));
  15.     offset >>= PAGE_SHIFT;

  16.     /*
  17.      * We try to allocate bootmem pages above 'goal'
  18.      * first, then we try to allocate lower pages.
  19.      */
  20.     if (goal && (goal >= bdata->node_boot_start) && ((goal >> PAGE_SHIFT) < bdata->node_low_pfn)) {
  21.         preferred = goal - bdata->node_boot_start;
  22.     } else
  23.         preferred = 0;

  24.     preferred = ((preferred + align - 1) & ~(align - 1)) >> PAGE_SHIFT;
  25.     preferred += offset;
  26.     areasize = (size+PAGE_SIZE-1)/PAGE_SIZE;
  27.     incr = align >> PAGE_SHIFT ? : 1;
  28.     //在bootm内存管理区中搜索位图,搜索找到第一个不为0的位图,并将位图的号保存
  29. restart_scan:
  30.     for (i = preferred; i < eidx; i += incr) {
  31.         unsigned long j;
  32.         if (test_bit(i, bdata->node_bootmem_map))     //跳过为1的位图
  33.             continue;
  34.         for (j = i + 1; j < i + areasize; ++j) {
  35.             if (j >= eidx)
  36.                 goto fail_block;
  37.             if (test_bit (j, bdata->node_bootmem_map))
  38.                 goto fail_block;
  39.         }
  40.         start = i;                           //前面reseved了两页内存-->第0页与第1页,所以这儿start=第2页
  41.         goto found;
  42.     fail_block:;
  43.     }
  44.     if (preferred) {
  45.         preferred = offset;
  46.         goto restart_scan;
  47.     }
  48.     return NULL;
  49. found:
  50.     if (start >= eidx)
  51.         BUG();

  52.     /*
  53.      * Is the next page of the previous allocation-end the start
  54.      * of this allocation's buffer? If yes then we can 'merge'
  55.      * the previous partial page with this allocation.
  56.      */
  57. //找到第1个不为0的位图后,按映射关系实际的物理地址=nr*4096,之后将物理地址转虚地址
  58. //注: nr不是数值是位号, 例0001中nr=0 -->0111中nr=2 -->1111中nr=3
  59.     if (align <= PAGE_SIZE && bdata->last_offset && bdata->last_pos+1 == start) {
  60.         offset = (bdata->last_offset+align-1) & ~(align-1);
  61.         if (offset > PAGE_SIZE)
  62.             BUG();
  63.         remaining_size = PAGE_SIZE-offset;
  64.         if (size < remaining_size) {
  65.             areasize = 0;
  66.             // last_pos unchanged
  67.             bdata->last_offset = offset+size;
  68.             ret = phys_to_virt(bdata->last_pos*PAGE_SIZE + offset bdata->node_boot_start);
  69.         } else {
  70.             remaining_size = size - remaining_size;
  71.             areasize = (remaining_size+PAGE_SIZE-1)/PAGE_SIZE;
  72.             ret = phys_to_virt(bdata->last_pos*PAGE_SIZE + offset + bdata->node_boot_start);
  73.             bdata->last_pos = start+areasize-1;
  74.             bdata->last_offset = remaining_size;
  75.         }
  76.         bdata->last_offset &= ~PAGE_MASK;
  77.     } else {
  78.         bdata->last_pos = start + areasize - 1;    //执行后bdata->last_pos=2
  79.         bdata->last_offset = size & ~PAGE_MASK;
  80.         ret = phys_to_virt(start * PAGE_SIZE + bdata->node_boot_start);    //start=2,bdata->node_boot_start=0,即将0x2000的实地址转虚地址0xc0002000
  81.     }
  82.     /*
  83.      * Reserve the area now:
  84.      */
  85.     for (i = start; i < start+areasize; i++)
  86.         if (test_and_set_bit(i, bdata->node_bootmem_map))      //将内存管理区位图中相应位置1
  87.             BUG();                                             //因为这是首次分配,所以是由0011-->0111 
  88.     memset(ret, 0, size);                          //将分配的内存区清0,ret=0xc0002000,size=4096
  89.     return ret;                                    //返回查找第1个不为0的位图的虚地址
  90. }
init_bootmem_core结束后,内存管理区的位图contig_page_data.bdata->node_bootmem_map=0xC0381000如下所示:
contig_page_data.bdata->node_bootmem_map=0xC0381000
  1. (gdb) x /300wb 0x381000
  2. 0x381000:    0x03    0x00    0x00    0x00    0x00    0x00    0x00    0x00    -->前两页是reserved所以是0011
  3. 0x381008:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
  4. 0x381010:    0x00    0x00    0x00    0x80    0xff    0xff    0xff    0xff
  5. 0x381018:    0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff
分配了一页内存之后,内存管理区的变化
  1. (gdb) x /300wb 0x381000
  2. 0x381000:    0x07    0x00    0x00    0x00    0x00    0x00    0x00    0x00     -->前两页是reserved,之后分配了一页所以是0111
  3. 0x381008:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
  4. 0x381010:    0x00    0x00    0x00    0x80    0xff    0xff    0xff    0xff
  5. 0x381018:    0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff
2.3.2 goal=0x1000000
setup_arch --> paging_init --> free_area_init --> free_area_init_core
    lmem_map = (struct page *) alloc_bootmem_node(pgdat, map_size);
  1. #define alloc_bootmem_node(pgdat, x) \
  2.     __alloc_bootmem_node((pgdat), (x), SMP_CACHE_BYTES, __pa(MAX_DMA_ADDRESS))
goal=0x1000000
  1. include/linux/cache.h:#define SMP_CACHE_BYTES L1_CACHE_BYTES    
  2. size=map_size=17825724=0x10FFFBC
  3. align=0x80
  4. goal=DMA的最大物理地址=0x01000000
  5. void * __init __alloc_bootmem_node (pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal)
  6. {
  7.     void *ptr;

  8.     ptr = __alloc_bootmem_core(pgdat->bdata, size, align, goal);
  9.     if (ptr)
  10.         return (ptr);
  11.     return NULL;
  12. }
分配结束后内存管理区的数据如下所示:
  1. (gdb) x /800bx 0x381000
  2. 0x3811f0:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
  3. 0x3811f8:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
  4. 0x381200:    0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff
  5. 0x381208:    0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff
0x381000是内存管理区的开始,0x381200是这次分配的内存管理数据的开始
我们按映射关系计算一下实际的物理地址:
0x381200-0x381000=0x200=512 --> 
512*8*4096=16M也就是说从物理地址16M开始向后按页分配内存

2.4 bootm内存的释放
在mm/bootmem.c中,将[addr--addr+size]所在的位图清0,其中addr与size都是物理地址的addr与size
  1. static void __init free_bootmem_core(bootmem_data_t *bdata, unsigned long addr, unsigned long size)
  2. {
  3.     unsigned long i;
  4.     unsigned long start;
  5.     /*
  6.      * round down end of usable mem, partially free pages are
  7.      * considered reserved.
  8.      */
  9.     unsigned long sidx;
  10. //注意:这儿只是除以PAGE_SIZE,而不是(num+PAGE_SIZE-1)/PAGE_SIZE,说明如果不满一页的部分不清0
  11.     unsigned long eidx = (addr + size - bdata->node_boot_start)/PAGE_SIZE;   //eidx是要清除的最大物理地址的相应页帧
  12.     unsigned long end = (addr + size)/PAGE_SIZE;

  13.     if (!size) BUG();
  14.     if (end > bdata->node_low_pfn)
  15.         BUG();
  16.     //start是要清除的物理地址相应页帧
  17.     start = (addr + PAGE_SIZE-1) / PAGE_SIZE;
  18.     sidx = start - (bdata->node_boot_start/PAGE_SIZE);
  19.     //将物理地址所对应的内存管理区的相应bit清0
  20.     for (i = sidx; i < eidx; i++) {
  21.         if (!test_and_clear_bit(i, bdata->node_bootmem_map))
  22.             BUG();
  23.     }
  24. }

2.5bootm的最后内存的释放
在mm/bootmem.c中,将[addr--addr+size]所在的位图清0,其中addr与size都是物理地址的addr与size
  1. static unsigned long __init free_all_bootmem_core(pg_data_t *pgdat)
  2. {
  3.     struct page *page = pgdat->node_mem_map;
  4.     bootmem_data_t *bdata = pgdat->bdata;
  5.     unsigned long i, count, total = 0;
  6.     unsigned long idx;

  7.     if (!bdata->node_bootmem_map) BUG();

  8.     count = 0;
  9.     idx = bdata->node_low_pfn - (bdata->node_boot_start >> PAGE_SHIFT);    //idx=0x38000,bdata->node_low_pfn=0x38000
  10. //从0->0x38000开始遍历内存管理区,将没有使用的页释放
  11.     for (i = 0; i < idx; i++, page++) {
  12.         if (!test_bit(i, bdata->node_bootmem_map)) {
  13.             count++;
  14.             ClearPageReserved(page);
  15.             set_page_count(page, 1);
  16.             __free_page(page);
  17.         }
  18.     }
  19.     total += count;

  20. //将内存管理区本身所占的内存释放
  21.     page = virt_to_page(bdata->node_bootmem_map);
  22.     count = 0;
  23.     for (i = 0; i < ((bdata->node_low_pfn-(bdata->node_boot_start >> PAGE_SHIFT))/8 + PAGE_SIZE-1)/PAGE_SIZE; i++,page++) {
  24.         count++;
  25.         ClearPageReserved(page);
  26.         set_page_count(page, 1);
  27.         __free_page(page);
  28.     }
  29.     total += count;
  30.     bdata->node_bootmem_map = NULL;

  31.     return total;
  32. }

附录1.将0xC0000000 取负放在unsigned long 中
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define __PAGE_OFFSET        (0xC0000000)
  4. #define __MAXMEM        (-__PAGE_OFFSET)

  5. int main ( int argc, char *argv[] )
  6. {
  7.     unsigned long a = __MAXMEM;
  8.     unsigned long b = __PAGE_OFFSET;
  9.     printf("a=%ld=0x%lx=0x%lxM=0x%lxG\n", a,a, a/1024/1024, a/1024/1024/1024);
  10.     printf("b=%ld=0x%lx=0x%lxM=0x%lxG\n", b,b, b/1024/1024, b/1024/1024/1024);
  11.     return EXIT_SUCCESS;
  12. }
运行结果:
  1. cong@msi:/work/os/test$ ./test
  2. a=1073741824=0x40000000=0x400M=0x1G
  3. b=-1073741824=0xc0000000=0xc00M=0x3G

阅读(1761) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~