linux的内存管理太过于庞大复杂了,复杂到有时候都不想细细分析下去了(即使我很喜欢专研代码),所以有时候就带着一些猜测。
我们知道在linux启动初始化的时候,bootargs传进来的内存信息,如mem=128mb@0x40000000,
内核调用bootmem_init_node->map_memory_bank->create_mapping将内存映射到3g开始的896MB空间内,
同时通过bitmap来在内核初始化的时候管理内存页面。
但是我们知道在linux中是通过伙伴系统来进行内存页面的管理的。所以必然有一个到buddy system的转换过程。
通过在CU论坛上与一个大牛的交流受到启发,再次读了一下代码发现以前的理解有下面偏差,我最初认为内存初始化时建立的页表会在内核初始化完成之前释放一些空闲页的相应项。但是细细想想,其实释放不释放是无所谓的,因为内核起来之后,所有的内存都归于伙伴系统管理。不释放反而省事。
内核在初始化的时候调用free_all_bootmem_core来初始化buddy system。
注意此时的环境:bitmap指示着内核中页面的使用情况,内存被映射到0XC0000000开始的内核空间中(页表已经建立)
- static unsigned long __init free_all_bootmem_core(pg_data_t *pgdat)
-
{
-
struct page *page;
-
unsigned long pfn;
-
bootmem_data_t *bdata = pgdat->bdata;
-
unsigned long i, count, total = 0;
-
unsigned long idx;
-
unsigned long *map;
-
int gofast = 0;
-
-
BUG_ON(!bdata->node_bootmem_map);
-
-
count = 0;
-
/* first extant page of the node */
-
pfn = bdata->node_boot_start >> PAGE_SHIFT;
-
idx = bdata->node_low_pfn - (bdata->node_boot_start >> PAGE_SHIFT);
-
map = bdata->node_bootmem_map;//该节点的位图
-
/* Check physaddr is O(LOG2(BITS_PER_LONG)) page aligned */
-
if (bdata->node_boot_start == 0 ||
-
ffs(bdata->node_boot_start) - PAGE_SHIFT > ffs(BITS_PER_LONG))
-
gofast = 1;
- //查看其实页面是不是2的幂次(小于32,因为一个页面1个bit,每次读出一个word,也就是32个页面)对齐的。
-
for (i = 0; i < idx; ) {
-
unsigned long v = ~map[i / BITS_PER_LONG];
- //根据是否对齐来释放页面,如果是对齐的,就释放32个页面
-
if (gofast && v == ~0UL) {
-
int order;
-
page = pfn_to_page(pfn);
-
count += BITS_PER_LONG;
-
order = ffs(BITS_PER_LONG) - 1;
-
__free_pages_bootmem(page, order);
-
i += BITS_PER_LONG;
-
page += BITS_PER_LONG;
-
} else if (v) {
- //如果不是对齐的,那么就一个页面一个页面的慢慢释放到伙伴系统中去
-
unsigned long m;
-
page = pfn_to_page(pfn);
-
for (m = 1; m && i < idx; m<<=1, page++, i++) {
-
if (v & m) {
-
count++;
-
__free_pages_bootmem(page, 0);
-
}
-
}
-
} else {
-
i+=BITS_PER_LONG;
-
}
-
pfn += BITS_PER_LONG;
-
}
-
total += count;
-
-
/*
-
* Now free the allocator bitmap itself, it's not
-
* needed anymore:
-
*/
- //释放掉位图所占用的内存空间,为什么呢。因为现在所有的内存都归于buddy system的管理,那么该位图就没有存在的必要了。因为位图是用来指示一个页面是否空闲的。既然伙伴系统存在了,空闲页面都是伙伴系统管理的。
-
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++;
-
__free_pages_bootmem(page, 0);
-
}
-
total += count;
-
bdata->node_bootmem_map = NULL;
-
-
return total;
-
}
内核根据已经存在的位图来判断相应的页面是否存在,将这些未使用的页面释放到伙伴系统中,关于伙伴系统如何回收页面在次不在描述。
到这儿结束的时候,伙伴系统已经建立,用于管理内存。但是注意内存初始化时建立的页表任然存在。
如果阅读仔细的话,可以发现在alloc_pages,kmalloc等这些内存分配代码中并没有设置页表的代码。因为页表已经存在了,无需设置。
我在内核中写了一段代码来测试了一下:
pg = alloc_pages(GFP_KERNEL,0);
if(!pg)
printk("alloc_pages failed \n");
ret = read_l(page_address(pg));
printk("ret = 0x%x\n",ret);
__free_pages(pg,0);
ret = read_l(page_address(pg));
printk("ret = 0x%x\n",ret);
上面的这段代码并没有发生段错误。那么linux中可以未经申请就能访问甚至修改一些内存空间的数据,不过好像也没什么危害。
阅读(846) | 评论(0) | 转发(0) |