一. 说明
1.1 内存参数的获取
a. 实模式下memory参数的获取
linux-2.4.18/arch/i386/boot/setup.S -->当时处于实模式下把E820的memory_map的结果放在了0x2D0处
-
linux-2.4.18/include/asm-i386/e820.h
-
#define E820MAP 0x2d0 /* our map */
-
#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内存管理区的内容如下所示
-
(gdb) x /200bx 0x381000
-
0x381000: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
-
0x381008: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
-
0x381010: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
-
....
-
0x388000: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
-
以上c完成后bootm内存管理区的内容如下所示
-
(gdb) x /200bx 0x381000
-
0x381000: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
-
0x381008: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
-
0x381010: 0x00 0x00 0x00 0x80 0xff 0xff 0xff 0xff
-
0x381018: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
-
0x381020: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
-
....
-
0x388000: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
以上d完成后bootm内存管理区的内容如下所示:
-
(gdb) x /500bx 0x381000
-
0x381000: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
-
0x381008: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
-
0x381010: 0x00 0x00 0x00 0x80 0xff 0xff 0xff 0xff
-
0x381018: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff -->这部分是[0x9FC00-1M]设为不可用
-
0x381020: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
-
0x381028: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
-
0x381030: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
-
0x381038: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
-
0x381040: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
-
0x381048: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
-
0x381050: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
-
0x381058: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
-
0x381060: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
-
0x381068: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff -->这部分是[1M-boom结束]设为不可用
-
0x381070: 0xff 0x01 0x00 0x00 0x00 0x00 0x00 0x00
-
....
-
0x388000: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
二.代码说明
2.1 内存参数的获取
linux-2.4.18/arch/i386/kernel/setup.c
-
void __init setup_arch(char **cmdline_p)
-
{
-
unsigned long bootmap_size, low_mem_size;
-
unsigned long start_pfn, max_pfn, max_low_pfn;
-
int i;
-
//以下是执行完setup_memory_region后的结果,qemu中设置了mem=256
-
//(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} }}
-
setup_memory_region();
-
-
init_mm.start_code = (unsigned long) &_text;
-
init_mm.end_code = (unsigned long) &_etext;
-
init_mm.end_data = (unsigned long) &_edata;
-
init_mm.brk = (unsigned long) &_end;
-
-
code_resource.start = virt_to_bus(&_text);
-
code_resource.end = virt_to_bus(&_etext)-1;
-
data_resource.start = virt_to_bus(&_etext);
-
data_resource.end = virt_to_bus(&_edata)-1;
-
-
parse_mem_cmdline(cmdline_p);
-
-
#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
-
#define PFN_DOWN(x) ((x) >> PAGE_SHIFT)
-
#define PFN_PHYS(x) ((x) << PAGE_SHIFT)
-
-
/*
-
* Reserved space for vmalloc and iomap - defined in asm/page.h
-
*/
-
#define MAXMEM_PFN PFN_DOWN(MAXMEM)
-
#define MAX_NONPAE_PFN (1 << 20)
-
-
//在System.map中0xc044daac A _end
-
//end的物理地址PA= 0xc044daac - 0xc000000 = 0x44daac
//#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
//内存管理的开始页帧start_pfn = (0x44daac+4k-1)/4k = 1102.666748047 =1102
-
start_pfn = PFN_UP(__pa(&_end));
-
//解析e820的map表,获取可用的mem的最高地址,我这儿1G的内存max_pfn=0x3fffe,1G内存最大的pfn=0x40000
-
//但最后两页0x2000不是可用mem,所以max_pfn=0x40000-2=0x3fffe
-
max_pfn = 0;
-
for (i = 0; i < e820.nr_map; i++) {
-
unsigned long start, end;
-
/* RAM? */
-
if (e820.map[i].type != E820_RAM) -->跳过保留内存区,#define E820_RESERVED 2
-
continue;
-
start = PFN_UP(e820.map[i].addr); -->将物理地址转为页帧号
-
end = PFN_DOWN(e820.map[i].addr + e820.map[i].size); -->将物理地址转为页帧号
-
if (start >= end)
-
continue;
-
if (end > max_pfn)
-
max_pfn = end; -->max_pfn可以理解为实际内存容量的页帧(根据e820map实际会减一点点)
-
}
-
-
//获取最高地址地页帧号,大于896M的内存其最高地址的页帧号就是896M的页帧
-
max_low_pfn = max_pfn;
-
if (max_low_pfn > MAXMEM_PFN) {
-
max_low_pfn = MAXMEM_PFN; //当内存大于896M时,max_low_pfn=896M的页帧=0x38000
-
#ifndef CONFIG_HIGHMEM
-
/* Maximum memory usable is what is directly addressable */
-
printk(KERN_WARNING "Warning only %ldMB will be used.\n", MAXMEM>>20);
-
if (max_pfn > MAX_NONPAE_PFN)
-
printk(KERN_WARNING "Use a PAE enabled kernel.\n");
-
else
-
printk(KERN_WARNING "Use a HIGHMEM enabled kernel.\n");
-
#else /* !CONFIG_HIGHMEM */
-
#ifndef CONFIG_X86_PAE
-
if (max_pfn > MAX_NONPAE_PFN) {
-
max_pfn = MAX_NONPAE_PFN;
-
printk(KERN_WARNING "Warning only 4GB will be used.\n");
-
printk(KERN_WARNING "Use a PAE enabled kernel.\n");
-
}
-
#endif /* !CONFIG_X86_PAE */
-
#endif /* !CONFIG_HIGHMEM */
-
}
-
-
#ifdef CONFIG_HIGHMEM
-
highstart_pfn = highend_pfn = max_pfn; -->max_pfn可以理解为实际内存容量的页帧=0x3fffe
-
if (max_pfn > MAXMEM_PFN) {
-
highstart_pfn = MAXMEM_PFN; -->highstart_pfn=0x38000(896M的页帧)
-
printk(KERN_NOTICE "%ldMB HIGHMEM available.\n",
-
pages_to_mb(highend_pfn - highstart_pfn));
-
}
-
#endif
-
//内存管理区的开始是内核end处,并页对齐的地址-->我这儿是0x381000
-
//当内存大于896M时,只用896M,内存管理区中1bit代表1页。
-
//bit[0]映射[0-4k]这块内存以此类推,bit[1]映射[4-8k]这块内存以此类推,
-
//最后将内存管理区全置1-->我这儿是将[0x381000-0x388000]这块内存全置1
-
bootmap_size = init_bootmem(start_pfn, max_low_pfn); -->bootmap_size=0x7000
-
-
//在init_bootmem中将内存管理区全置1,代表己被占用,哪些内存可以用呢?
-
//答案是在e802的mem_map中属性为E820_RAM的是可以用的
-
//下面这个for循环查找e820的mem_map找到属性为E820_RAM的内存
-
//将这部分内存在内存管理区中置0,代表这块内存可用
-
for (i = 0; i < e820.nr_map; i++) {
-
unsigned long curr_pfn, last_pfn, size;
-
if (e820.map[i].type != E820_RAM)
-
continue;
-
curr_pfn = PFN_UP(e820.map[i].addr);
-
if (curr_pfn >= max_low_pfn)
-
continue;
-
last_pfn = PFN_DOWN(e820.map[i].addr + e820.map[i].size);
-
-
if (last_pfn > max_low_pfn) -->如果这个内存区结束地址大于896M
-
last_pfn = max_low_pfn; -->则设置内存区结束地址为896M-->并转为页帧
-
if (last_pfn <= curr_pfn)
-
continue;
-
-
size = last_pfn - curr_pfn;
-
free_bootmem(PFN_PHYS(curr_pfn), PFN_PHYS(size)); -->将内存管理区中[0-896M]中可用部分置0
-
}
-
//将内核及内存管理区所在的内存 在内存管理区相应的映射地址处置1 -->表明这部分内存不可用
-
//内核的end=380150,页对齐=0x381000, 整个不可用部分的size=(0x381000-0x100000)+0x7000=0x288000
-
//不可用部分的起始地址=0x100000=1M转为页帧256, 长度0x288000转为页帧=0x288000/4096=648
-
//转为位图0x381000+256/8=0x20 648/8=81 [0x381000+0x20 --- +81]这个内存管理区置1
-
//即将[0x381020-0x0x381071]置1
-
reserve_bootmem(HIGH_MEMORY, (PFN_PHYS(start_pfn) + bootmap_size + PAGE_SIZE-1) - (HIGH_MEMORY));
-
-
reserve_bootmem(0, PAGE_SIZE); -->将[0-4K]设为不可用 -->内存管理区0x381000: 0x01
-
-
#ifdef CONFIG_SMP
-
reserve_bootmem(PAGE_SIZE, PAGE_SIZE); -->将[4K-8K]设为不可用 -->内存管理区0x381000: 0x03
-
#endif
-
.... --->到此己经可以用bootm来进行内存分配了
-
}
注: 关于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
-
--> 896*1024*1024/4096=229376=0x38000
--> 需要用byte的位图 380000/8=28672=0x7000,所以内存管理区的大小是0x7000个字节,管理内存是896M
-->将[0x381000-0x388000]这块内存全置1
-
//mapstart--> 是内核结束的地址(按页对齐)的页帧 ,这个就是内存的位图管理区的始地址(我这儿0x381000)
//start -->是 0
//end --> 是内存结束或896M的页帧
-
static unsigned long __init init_bootmem_core (pg_data_t *pgdat, unsigned long mapstart, unsigned long start, unsigned long end)
-
{
-
bootmem_data_t *bdata = pgdat->bdata;
-
unsigned long mapsize = ((end - start)+7)/8; -->mem的管理是按位图来管理的,1位代表1页
-
-
pgdat->node_next = pgdat_list;
-
pgdat_list = pgdat;
-
-
mapsize = (mapsize + (sizeof(long) - 1UL)) & ~(sizeof(long) - 1UL);
-
bdata->node_bootmem_map = phys_to_virt(mapstart << PAGE_SHIFT); //将位图管理区的起始地址(0xc0381000)转为虚地址
-
bdata->node_boot_start = (start << PAGE_SHIFT);
-
bdata->node_low_pfn = end;
-
-
/*
-
* Initially all pages are reserved - setup_arch() has to
-
* register free RAM areas explicitly.
-
*/
-
memset(bdata->node_bootmem_map, 0xff, mapsize); -->将位图所有位置1,表明己被占用
-
-
return mapsize;
-
}
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
-
#define alloc_bootmem_low_pages(x) \
-
__alloc_bootmem((x), PAGE_SIZE, 0)
goal=0
-
static void * __init __alloc_bootmem_core (bootmem_data_t *bdata,
-
unsigned long size, unsigned long align, unsigned long goal)
-
{
-
unsigned long i, start = 0;
-
void *ret;
-
unsigned long offset, remaining_size;
-
unsigned long areasize, preferred, incr;
-
unsigned long eidx = bdata->node_low_pfn - (bdata->node_boot_start >>PAGE_SHIFT); //eidx=0x38000=896M的页帧
-
-
if (!size) BUG();
-
-
if (align & (align-1))
-
BUG();
-
-
offset = 0;
-
if (align && (bdata->node_boot_start & (align - 1UL)) != 0)
-
offset = (align - (bdata->node_boot_start & (align - 1UL)));
-
offset >>= PAGE_SHIFT;
-
-
/*
-
* We try to allocate bootmem pages above 'goal'
-
* first, then we try to allocate lower pages.
-
*/
-
if (goal && (goal >= bdata->node_boot_start) && ((goal >> PAGE_SHIFT) < bdata->node_low_pfn)) {
-
preferred = goal - bdata->node_boot_start;
-
} else
-
preferred = 0;
-
-
preferred = ((preferred + align - 1) & ~(align - 1)) >> PAGE_SHIFT;
-
preferred += offset;
-
areasize = (size+PAGE_SIZE-1)/PAGE_SIZE;
-
incr = align >> PAGE_SHIFT ? : 1;
-
//在bootm内存管理区中搜索位图,搜索找到第一个不为0的位图,并将位图的号保存
-
restart_scan:
-
for (i = preferred; i < eidx; i += incr) {
-
unsigned long j;
-
if (test_bit(i, bdata->node_bootmem_map)) //跳过为1的位图
-
continue;
-
for (j = i + 1; j < i + areasize; ++j) {
-
if (j >= eidx)
-
goto fail_block;
-
if (test_bit (j, bdata->node_bootmem_map))
-
goto fail_block;
-
}
-
start = i; //前面reseved了两页内存-->第0页与第1页,所以这儿start=第2页
-
goto found;
-
fail_block:;
-
}
-
if (preferred) {
-
preferred = offset;
-
goto restart_scan;
-
}
-
return NULL;
-
found:
-
if (start >= eidx)
-
BUG();
-
-
/*
-
* Is the next page of the previous allocation-end the start
-
* of this allocation's buffer? If yes then we can 'merge'
-
* the previous partial page with this allocation.
-
*/
-
//找到第1个不为0的位图后,按映射关系实际的物理地址=nr*4096,之后将物理地址转虚地址
-
//注: nr不是数值是位号, 例0001中nr=0 -->0111中nr=2 -->1111中nr=3
-
if (align <= PAGE_SIZE && bdata->last_offset && bdata->last_pos+1 == start) {
-
offset = (bdata->last_offset+align-1) & ~(align-1);
-
if (offset > PAGE_SIZE)
-
BUG();
-
remaining_size = PAGE_SIZE-offset;
-
if (size < remaining_size) {
-
areasize = 0;
-
// last_pos unchanged
-
bdata->last_offset = offset+size;
-
ret = phys_to_virt(bdata->last_pos*PAGE_SIZE + offset + bdata->node_boot_start);
-
} else {
-
remaining_size = size - remaining_size;
-
areasize = (remaining_size+PAGE_SIZE-1)/PAGE_SIZE;
-
ret = phys_to_virt(bdata->last_pos*PAGE_SIZE + offset + bdata->node_boot_start);
-
bdata->last_pos = start+areasize-1;
-
bdata->last_offset = remaining_size;
-
}
-
bdata->last_offset &= ~PAGE_MASK;
-
} else {
-
bdata->last_pos = start + areasize - 1; //执行后bdata->last_pos=2
-
bdata->last_offset = size & ~PAGE_MASK;
-
ret = phys_to_virt(start * PAGE_SIZE + bdata->node_boot_start); //start=2,bdata->node_boot_start=0,即将0x2000的实地址转虚地址0xc0002000
-
}
-
/*
-
* Reserve the area now:
-
*/
-
for (i = start; i < start+areasize; i++)
-
if (test_and_set_bit(i, bdata->node_bootmem_map)) //将内存管理区位图中相应位置1
-
BUG(); //因为这是首次分配,所以是由0011-->0111
-
memset(ret, 0, size); //将分配的内存区清0,ret=0xc0002000,size=4096
-
return ret; //返回查找第1个不为0的位图的虚地址
-
}
init_bootmem_core结束后,内存管理区的位图contig_page_data.bdata->node_bootmem_map=0xC0381000如下所示:
contig_page_data.bdata->node_bootmem_map=0xC0381000
-
(gdb) x /300wb 0x381000
-
0x381000: 0x03 0x00 0x00 0x00 0x00 0x00 0x00 0x00 -->前两页是reserved所以是0011
-
0x381008: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
-
0x381010: 0x00 0x00 0x00 0x80 0xff 0xff 0xff 0xff
-
0x381018: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
分配了一页内存之后,内存管理区的变化
-
(gdb) x /300wb 0x381000
-
0x381000: 0x07 0x00 0x00 0x00 0x00 0x00 0x00 0x00 -->前两页是reserved,之后分配了一页所以是0111
-
0x381008: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
-
0x381010: 0x00 0x00 0x00 0x80 0xff 0xff 0xff 0xff
-
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);
-
#define alloc_bootmem_node(pgdat, x) \
-
__alloc_bootmem_node((pgdat), (x), SMP_CACHE_BYTES, __pa(MAX_DMA_ADDRESS))
goal=0x1000000
-
include/linux/cache.h:#define SMP_CACHE_BYTES L1_CACHE_BYTES
-
size=map_size=17825724=0x10FFFBC
-
align=0x80
-
goal=DMA的最大物理地址=0x01000000
-
void * __init __alloc_bootmem_node (pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal)
-
{
-
void *ptr;
-
-
ptr = __alloc_bootmem_core(pgdat->bdata, size, align, goal);
-
if (ptr)
-
return (ptr);
-
return NULL;
-
}
分配结束后内存管理区的数据如下所示:
-
(gdb) x /800bx 0x381000
-
0x3811f0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
-
0x3811f8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
-
0x381200: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
-
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
-
static void __init free_bootmem_core(bootmem_data_t *bdata, unsigned long addr, unsigned long size)
-
{
-
unsigned long i;
-
unsigned long start;
-
/*
-
* round down end of usable mem, partially free pages are
-
* considered reserved.
-
*/
-
unsigned long sidx;
-
//注意:这儿只是除以PAGE_SIZE,而不是(num+PAGE_SIZE-1)/PAGE_SIZE,说明如果不满一页的部分不清0
-
unsigned long eidx = (addr + size - bdata->node_boot_start)/PAGE_SIZE; //eidx是要清除的最大物理地址的相应页帧
-
unsigned long end = (addr + size)/PAGE_SIZE;
-
-
if (!size) BUG();
-
if (end > bdata->node_low_pfn)
-
BUG();
-
//start是要清除的物理地址相应页帧
-
start = (addr + PAGE_SIZE-1) / PAGE_SIZE;
-
sidx = start - (bdata->node_boot_start/PAGE_SIZE);
-
//将物理地址所对应的内存管理区的相应bit清0
-
for (i = sidx; i < eidx; i++) {
-
if (!test_and_clear_bit(i, bdata->node_bootmem_map))
-
BUG();
-
}
-
}
2.5bootm的最后内存的释放
在mm/bootmem.c中,将[addr--addr+size]所在的位图清0,其中addr与size都是物理地址的addr与size
-
static unsigned long __init free_all_bootmem_core(pg_data_t *pgdat)
-
{
-
struct page *page = pgdat->node_mem_map;
-
bootmem_data_t *bdata = pgdat->bdata;
-
unsigned long i, count, total = 0;
-
unsigned long idx;
-
-
if (!bdata->node_bootmem_map) BUG();
-
-
count = 0;
-
idx = bdata->node_low_pfn - (bdata->node_boot_start >> PAGE_SHIFT); //idx=0x38000,bdata->node_low_pfn=0x38000
-
//从0->0x38000开始遍历内存管理区,将没有使用的页释放
-
for (i = 0; i < idx; i++, page++) {
-
if (!test_bit(i, bdata->node_bootmem_map)) {
-
count++;
-
ClearPageReserved(page);
-
set_page_count(page, 1);
-
__free_page(page);
-
}
-
}
-
total += count;
-
-
//将内存管理区本身所占的内存释放
-
page = virt_to_page(bdata->node_bootmem_map);
-
count = 0;
-
for (i = 0; i < ((bdata->node_low_pfn-(bdata->node_boot_start >> PAGE_SHIFT))/8 + PAGE_SIZE-1)/PAGE_SIZE; i++,page++) {
-
count++;
-
ClearPageReserved(page);
-
set_page_count(page, 1);
-
__free_page(page);
-
}
-
total += count;
-
bdata->node_bootmem_map = NULL;
-
-
return total;
-
}
附录1.将0xC0000000 取负放在unsigned long 中
-
#include <stdio.h>
-
#include <stdlib.h>
-
#define __PAGE_OFFSET (0xC0000000)
-
#define __MAXMEM (-__PAGE_OFFSET)
-
-
int main ( int argc, char *argv[] )
-
{
-
unsigned long a = __MAXMEM;
-
unsigned long b = __PAGE_OFFSET;
-
printf("a=%ld=0x%lx=0x%lxM=0x%lxG\n", a,a, a/1024/1024, a/1024/1024/1024);
-
printf("b=%ld=0x%lx=0x%lxM=0x%lxG\n", b,b, b/1024/1024, b/1024/1024/1024);
-
return EXIT_SUCCESS;
-
}
运行结果:
-
cong@msi:/work/os/test$ ./test
-
a=1073741824=0x40000000=0x400M=0x1G
-
b=-1073741824=0xc0000000=0xc00M=0x3G
阅读(1761) | 评论(0) | 转发(0) |