uclinux2.6(bf561)中的bootmem分析(4):alloc_bootmem_pages

 
  
这是一个定义好的宏,用于分配内存:
#define alloc_bootmem_pages(x) /
     __alloc_bootmem(x, PAGE_SIZE, __pa(MAX_DMA_ADDRESS))
其中MAX_DMA_ADDRESS的定义为:
#define MAX_DMA_ADDRESS PAGE_OFFSET
而PAGE_OFFSET是定义成0的。
__pa的定义为:
#define __pa(vaddr)         virt_to_phys((void *)(vaddr))
virt_to_phys的定义为:
#define virt_to_phys(vaddr) ((unsigned long) (vaddr))
实际上,alloc_bootmem_pages这个宏就变成了
#define alloc_bootmem_pages(x) /
     __alloc_bootmem(x, PAGE_SIZE, 0)
看看 __alloc_bootmem的实现代码:
 
void * __init __alloc_bootmem(unsigned long size, unsigned long align,
                    unsigned long goal)
{
     void *mem = __alloc_bootmem_nopanic(size,align,goal);
 
     if (mem)
         return mem;
     /*
      * Whoops, we cannot satisfy the allocation request.
      */
     printk(KERN_ALERT "bootmem alloc of %lu bytes failed!/n", size);
     panic("Out of memory");
     return NULL;
}
再看看的 __alloc_bootmem_nopanic实现:
 
void * __init __alloc_bootmem_nopanic(unsigned long size, unsigned long align,
                         unsigned long goal)
{
     bootmem_data_t *bdata;
     void *ptr;
 
     list_for_each_entry(bdata, &bdata_list, list) {
         ptr = __alloc_bootmem_core(bdata, size, align, goal, 0);
         if (ptr)
              return ptr;
     }
     return NULL;
}
前面说过bdata_list实际上只有一个元素,因此这一循环将只执行一次, __alloc_bootmem_core的实现在bootmem.c中,代码较长。先看看注释和声明:
 
/*
 * We 'merge' subsequent allocations to save space. We might 'lose'
 * some fraction of a page if allocations cannot be satisfied due to
 * size constraints on boxes where there is physical RAM space
 * fragmentation - in these cases (mostly large memory boxes) this
 * is not a problem.
 *
 * On low memory boxes we get it right in 100% of the cases.
 *
 * alignment has to be a power of 2 value.
 *
 * NOTE: This function is _not_ reentrant.
 */
void * __init
__alloc_bootmem_core(struct bootmem_data *bdata, unsigned long size,
           unsigned long align, unsigned long goal, unsigned long limit)
{
当调用到这里的时候goal和limit的值都为0,align为页面大小4K。
以下代码显示了__alloc_bootmem的页面分配策略:
restart_scan:
     for (i = preferred; i < eidx; i += incr) {
         unsigned long j;
         i = find_next_zero_bit(bdata->node_bootmem_map, eidx, i);
         i = ALIGN(i, incr);
         if (i >= eidx)
              break;
         if (test_bit(i, bdata->node_bootmem_map))
              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;
         goto found;
     fail_block:
         i = ALIGN(j, incr);
     }
在上面的代码中,prefeered为0,areasize表示总共要求分配的页数,incr取1。很简单,就是沿着页表从低往高找,找到第一个可用的页块。
当分配成功后,此函数将记录这次分配的信息:
found:
     bdata->last_success = PFN_PHYS(start);
     BUG_ON(start >= eidx);
 
     /*
      * 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.
      */
     if (align < PAGE_SIZE &&
         bdata->last_offset && bdata->last_pos+1 == start) {
        
     } else {
         bdata->last_pos = start + areasize - 1;
         bdata->last_offset = size & ~PAGE_MASK;
         ret = phys_to_virt(start * PAGE_SIZE + bdata->node_boot_start);
     }
 
     /*
      * Reserve the area now:
      */
     for (i = start; i < start + areasize; i++)
         if (unlikely(test_and_set_bit(i, bdata->node_bootmem_map)))
              BUG();
     memset(ret, 0, size);
     return ret;
还有一个值得注意的是在分配成功之后,此函数还负责将这段内存清0。

你可能感兴趣的:(list,null,constraints,merge,alignment,Allocation)