1.
// 按照 node_min_pfn 从小到大的顺序串联 bootmem_data_t
// 发现这个链表实际是将整个内存区域给链在了一起,如果两块64M的内存(两个Node),那么每一块用bootmem_data_t结构体来描述,然后这个链表将这两个描述链在一起
static void __init link_bootmem(bootmem_data_t *bdata)
{
struct list_head *iter;
list_for_each(iter, &bdata_list) {
bootmem_data_t *ent;
ent = list_entry(iter, bootmem_data_t, list);
if (bdata->node_min_pfn < ent->node_min_pfn)
break;
}
list_add_tail(&bdata->list, iter);////将节点挂在本节点前,返回
}
static unsigned long __init init_bootmem_core(bootmem_data_t *bdata,
unsigned long mapstart, unsigned long start, unsigned long end)
{
unsigned long mapsize;
mminit_validate_memmodel_limits(&start, &end);
bdata->node_bootmem_map = phys_to_virt(PFN_PHYS(mapstart));/*存储位图起始地址的虚拟地址*/
bdata->node_min_pfn = start;/*节点中的起始页*/
bdata->node_low_pfn = end; /*节点中的终止页*/
link_bootmem(bdata);/*将该bdata按顺序链入bdata_list中*/
/*
* Initially all pages are reserved - setup_arch() has to
* register free RAM areas explicitly.
*/
mapsize = bootmap_bytes(end - start); //计算所需要的最少字节数。有mapsize这么大的字节数,就可以使用每比特去描述每个page
//此时,node_bootmem_map开始的每一个bit,就代表一个对应的页的状态是否是保留状态;这些可以由 test_and_set_bit\test_and_clear_bit\test_bit等进行查看或者设置
memset(bdata->node_bootmem_map, 0xff, mapsize);/*将位图全部置1,保留所有页*/
bdebug("nid=%td start=%lx map=%lx end=%lx mapsize=%lx\n",
bdata - bootmem_node_data, start, mapstart, end, mapsize);
return mapsize;/*返回位图大小*/
}
static inline int test_and_set_bit(int nr, volatile unsigned long *addr)//从addr开始,往后数nr个bit,选中这个bit,然后将这个bit设置为1,将这个bit的old值返回
static inline int test_and_clear_bit(int nr, volatile unsigned long *addr)//从addr开始,往后数nr个bit,选中这个bit,然后将这个bit设置为0,将这个bit的old值返回
static inline int test_bit(int nr, const volatile unsigned long *addr)//从addr开始,往后数nr个bit,选中这个bit,然后测试这个bit是否为1
alloc_bootmem_core(struct bootmem_data *bdata,
unsigned long size, //申请的大小
unsigned long align, //对齐字节
unsigned long goal, //开始地址(全局地址,如无则从bdata开始地址起)
unsigned long limit //操作的最大地址
)
{
..........................
//此句之前的其实都是在物理内存的数据结构中进行查找的,一旦查找到了,那么就将起始地址转换成线性地址,最后返回的就是线性地址
region = phys_to_virt(PFN_PHYS(bdata->node_min_pfn) + /*得到起始地址的虚拟地址*/
start_off);
memset(region, 0, size);/*将申请到的区域清空*/
/*
* The min_count is set to 0 so that bootmem allocated blocks
* are never reported as leaks.
*/
kmemleak_alloc(region, size, 0, 0);
return region;
}
//
static unsigned long __init bootmap_bytes(unsigned long pages)
{
unsigned long bytes = (pages + 7) / 8; // 这个是page的数量对8向上取整数,表示至少需要bytes个字节,可以每比特描述一个page
return ALIGN(bytes, sizeof(long)); // ??? free_all_bootmem_core中对vec的取值,需要一次搞一个long型的,所以此处需要对long对齐
}
//使用比特来描述page是保留与否的状态,那么有多少页就需要多少比特,使用能满足的最少页面数对应的内存容量来使用这些bit
unsigned long __init bootmem_bootmap_pages(unsigned long pages) //为了计算pages数量的内存页,至少需要多少页面对应的内存,才能进行内存的每比特描述一个page
{
unsigned long bytes = bootmap_bytes(pages);
return PAGE_ALIGN(bytes) >> PAGE_SHIFT; // bytes对4K向上取整数,表示至少需要这么多页面对应的内存可以提供 内存的每比特描述一个page
}
/////////////////////////////////////////////////////////////////
static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata)
{
int aligned;
struct page *page;
unsigned long start, end, pages, count = 0;
if (!bdata->node_bootmem_map)/*bitmap不存在,表示该节点已经释放*/
return 0;
/*获得低端内存的起始页框和终止页框*/
start = bdata->node_min_pfn;
end = bdata->node_low_pfn;
/*
* If the start is aligned to the machines wordsize, we might
* be able to free pages in bulks of that order.
*/
aligned = !(start & (BITS_PER_LONG - 1));// 这个应该是判断 start的值 是否是 本机器的字节宽度的整数倍,32位机器对应的是 32 比特 对齐
bdebug("nid=%td start=%lx end=%lx aligned=%d\n",
bdata - bootmem_node_data, start, end, aligned);
/*************************************
* 第一步:释放空闲页 *
*************************************/
while (start < end) {
unsigned long *map, idx, vec;
map = bdata->node_bootmem_map;
idx = start - bdata->node_min_pfn;
vec = ~map[idx / BITS_PER_LONG];/*将idx所处的long字段的位图部分进行取反*/ //这个操作还需要仔细研究,参考bootmap_bytes对long对齐这一步
//根据bootmap_bytes的实现,可以看到node_bootmem_map开始的每个字节,都是对8个page的描述。而且需要的字节数根据处理后,是以long对齐的。
//idx表示的是某个page,对应到node_bootmem_map开始的某个字节的某个比特上,所以使用 idx / BITS_PER_LONG 可以计算这个idx对应到哪个long型上,然后可以在map中取到这个long值,然后按位取反赋给vec
//map中的每个bit位都是描述对应的page的;使用此处的操作,是将idx所在的连续的32个元素取出来,每个元素肯定是0或者1,那么就可以使用这32个0/1组成一个long型数值,存入vec中
// bit 0 1 2 3 4 5 6 7 8...... 31 32 33 34 35 36 ... 63 64 65 ...
// byte | 0 | |1 | |2|。。。。。。。。。。。。。。。。。。。。。
// long | 0 | | 1 | | 2..............
/*如果:1.起始地址是2的整数幂
2.该long字段的位图全为0,即空闲状态
3.start+BITS_PER_LONG未超过范围*/
if (aligned && vec == ~0UL && start + BITS_PER_LONG < end) {
int order = ilog2(BITS_PER_LONG);/*得到Long的长度为2的多少次幂*/
__free_pages_bootmem(pfn_to_page(start), order);/*直接将整块内存释放*/ // order = 5或者6, 32、64个page一次性释放掉
count += BITS_PER_LONG; // count统计总共释放的page数,此时可以添加 32、64个
} else {/*否则只能逐页释放*/
unsigned long off = 0;
while (vec && off < BITS_PER_LONG) {/*判断该字段内的空闲页是否已经释放完*/
if (vec & 1) { /*vec的最低位为1,也就是说start+off对应的page为空闲*/ //vec是long型,每一个bit都是对一个page的描述,先从最低的bit开始判断,然后vec不停右移,计算更高位的bit
page = pfn_to_page(start + off); //vec & 1 就是取vec的最低一位,判断描述bit是否是0,是的话才能释放的
__free_pages_bootmem(page, 0);
count++;
}
vec >>= 1; // vec不停右移,更高位的bit被移动到最低位,这样在下次计算 vec & 1时,取到最低位
off++;
}
}
start += BITS_PER_LONG; // ???此处有个疑问,如果end与start的差距不是 32的整数倍,那么内层while循环,会导致 多释放页面吗?此时 start+32 超过了end,进入else分支,但是使用off < 32 来判断,所以会多释放 (32-(end-start))个page啊
}
/*****************************
* 第二步:释放保存bitmap的页 * // 在bootmem_bootmap_pages中可以计算pages个页面需要多少内存,将需要的内存用page(4K)的个数表示
******************************/
page = virt_to_page(bdata->node_bootmem_map);/*得到bitmap起始地址的所属页*/
pages = bdata->node_low_pfn - bdata->node_min_pfn;
pages = bootmem_bootmap_pages(pages);/*得到bitmap的大小,以页为单位*/ //这两步的pages是不同的含义,入参的pages是 map图需要表示的页面数量,返回的值是map图占用的内存是多大,需要多少个page(4K)
count += pages; // map图自身占用的内存是多少个page,释放掉,此时count就又增加了这么多个page
while (pages--)/*逐页释放*/
__free_pages_bootmem(page++, 0);
bdebug("nid=%td released=%lx\n", bdata - bootmem_node_data, count);
return count;/*返回释放的页框数*/
}
阅读(792) | 评论(0) | 转发(0) |