内存管理[3]

 

    vmalloc()函数的工作方式类似于kmalloc(),只不过前者分配的内存虚拟地址是连续的,而物理地址则无需连续。这也是用户空间分配函数的工作方式:由malloc()返回的页在进程的虚拟地址空间内是连续的,但是,这并不保证它们在物理RAM中也连续。kmalloc()函数确保页在物理地址上是连续的(虚拟地址自然也是连续的)。vmalloc()函数只确保页在虚拟地址空间内是连续的。它通过分配非连续的物理内存块,再“修正”页表,把内存映射到逻辑地址空间的连续区域中,就能做到这一点。

   一般只有硬件设备需要得到物理地址连续的内存。在很多体系结构上,硬件设备存在于内存管理单元以外,它根本不理解什么是虚拟地址。因此,硬件设备用到的任何内存区都必须是物理上连续的块,而不仅仅是虚拟地址连续的块。而仅供软件使用的内存块(例如与进程相关的缓冲区)就可以使用只有虚拟地址连续的内存块。对内核而言,所有内存看起来都是逻辑上连续的。

   尽管紧急在某些情况下才需要物理上连续的内存块,但是,很多内核代码都用kmalloc()来获得内存,而不是vmalloc()。因为vmalloc()函数为了把物理上不连续的页转换为虚拟地址空间上连续的页,必须专门建立页表项。通过vmalloc()获得的页必须一个一个地进行映射,这就导致比直接内存映射大得多的TLB抖动。因此,vmalloc()只在不得已的时候才用,一般是为了获得大块内存时,如,当模块被动态的插入到内核中,就把模块装载到由vmalloc()分配的内存上。

   

  1. 在<Vmalloc.c(mm)>
  2. /**
  3.  *  vmalloc  -  allocate virtually contiguous memory
  4.  *  @size:      allocation size
  5.  *  Allocate enough pages to cover @size from the page level
  6.  *  allocator and map them into contiguous kernel virtual space.
  7.  *
  8.  *  For tight control over page level allocator and protection flags
  9.  *  use __vmalloc() instead.
  10.  */
  11. void *vmalloc(unsigned long size)
  12. {
  13.     return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL);
  14. }
  15. void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
  16. {
  17.     return __vmalloc_node(size, gfp_mask, prot, -1);
  18. }
  19. /**
  20.  *  __vmalloc_node  -  allocate virtually contiguous memory
  21.  *  @size:      allocation size
  22.  *  @gfp_mask:  flags for the page level allocator
  23.  *  @prot:      protection mask for the allocated pages
  24.  *  @node:      node to use for allocation or -1
  25.  *
  26.  *  Allocate enough pages to cover @size from the page level
  27.  *  allocator with @gfp_mask flags.  Map them into contiguous
  28.  *  kernel virtual space, using a pagetable protection of @prot.
  29.  */
  30. static void *__vmalloc_node(unsigned long size, gfp_t gfp_mask, pgprot_t prot,
  31.                 int node)
  32. {
  33.     struct vm_struct *area;
  34.     size = PAGE_ALIGN(size);
  35.     if (!size || (size >> PAGE_SHIFT) > num_physpages)
  36.         return NULL;
  37.     area = get_vm_area_node(size, VM_ALLOC, node, gfp_mask);
  38.     if (!area)
  39.         return NULL;
  40.     return __vmalloc_area_node(area, gfp_mask, prot, node);
  41. }
  42. struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags,
  43.                 unsigned long start, unsigned long end)
  44. {
  45.     return __get_vm_area_node(size, flags, start, end, -1, GFP_KERNEL);
  46. }
  47. static struct vm_struct *__get_vm_area_node(unsigned long size, unsigned long flags,
  48.                         unsigned long start, unsigned long end,
  49.                         int node, gfp_t gfp_mask)
  50. {
  51.     struct vm_struct **p, *tmp, *area;
  52.     unsigned long align = 1;
  53.     unsigned long addr;
  54.     BUG_ON(in_interrupt());
  55.     if (flags & VM_IOREMAP) {
  56.         int bit = fls(size);
  57.         if (bit > IOREMAP_MAX_ORDER)
  58.             bit = IOREMAP_MAX_ORDER;
  59.         else if (bit < PAGE_SHIFT)
  60.             bit = PAGE_SHIFT;
  61.         align = 1ul << bit;
  62.     }
  63.     addr = ALIGN(start, align);
  64.     size = PAGE_ALIGN(size);
  65.     if (unlikely(!size))
  66.         return NULL;
  67.     area = kmalloc_node(sizeof(*area), gfp_mask & GFP_LEVEL_MASK, node);
  68.     if (unlikely(!area))
  69.         return NULL;
  70.     /*
  71.      * We always allocate a guard page.
  72.      */
  73.     size += PAGE_SIZE;
  74.     write_lock(&vmlist_lock);
  75.     for (p = &vmlist; (tmp = *p) != NULL ;p = &tmp->next) {
  76.         if ((unsigned long)tmp->addr < addr) {
  77.             if((unsigned long)tmp->addr + tmp->size >= addr)
  78.                 addr = ALIGN(tmp->size + 
  79.                          (unsigned long)tmp->addr, align);
  80.             continue;
  81.         }
  82.         if ((size + addr) < addr)
  83.             goto out;
  84.         if (size + addr <= (unsigned long)tmp->addr)
  85.             goto found;
  86.         addr = ALIGN(tmp->size + (unsigned long)tmp->addr, align);
  87.         if (addr > end - size)
  88.             goto out;
  89.     }
  90. found:
  91.     area->next = *p;
  92.     *p = area;
  93.     area->flags = flags;
  94.     area->addr = (void *)addr;
  95.     area->size = size;
  96.     area->pages = NULL;
  97.     area->nr_pages = 0;
  98.     area->phys_addr = 0;
  99.     write_unlock(&vmlist_lock);
  100.     return area;
  101. out:
  102.     write_unlock(&vmlist_lock);
  103.     kfree(area);
  104.     if (printk_ratelimit())
  105.         printk(KERN_WARNING "allocation failed: out of vmalloc space - use vmalloc=<size> to increase size./n");
  106.     return NULL;
  107. }

   函数可能睡眠,因此不能在中断上下文进行调用,也不能在其他不允许阻塞情况下调用。

   要释放通过vmalloc()函数所获得的内存,使用下面的函数:

  1. /**
  2.  *  vfree  -  release memory allocated by vmalloc()
  3.  *  @addr:      memory base address
  4.  *
  5.  *  Free the virtually contiguous memory area starting at @addr, as
  6.  *  obtained from vmalloc(), vmalloc_32() or __vmalloc(). If @addr is
  7.  *  NULL, no operation is performed.
  8.  *
  9.  *  Must not be called in interrupt context.
  10.  */
  11. void vfree(void *addr)
  12. {
  13.     BUG_ON(in_interrupt());
  14.     __vunmap(addr, 1);
  15. }
  16. void __vunmap(void *addr, int deallocate_pages)
  17. {
  18.     struct vm_struct *area;
  19.     if (!addr)
  20.         return;
  21.     if ((PAGE_SIZE-1) & (unsigned long)addr) {
  22.         printk(KERN_ERR "Trying to vfree() bad address (%p)/n", addr);
  23.         WARN_ON(1);
  24.         return;
  25.     }
  26.     area = remove_vm_area(addr);
  27.     if (unlikely(!area)) {
  28.         printk(KERN_ERR "Trying to vfree() nonexistent vm area (%p)/n",
  29.                 addr);
  30.         WARN_ON(1);
  31.         return;
  32.     }
  33.     debug_check_no_locks_freed(addr, area->size);
  34.     if (deallocate_pages) {
  35.         int i;
  36.         for (i = 0; i < area->nr_pages; i++) {
  37.             BUG_ON(!area->pages[i]);
  38.             __free_page(area->pages[i]);
  39.         }
  40.         if (area->flags & VM_VPAGES)
  41.             vfree(area->pages);
  42.         else
  43.             kfree(area->pages);
  44.     }
  45.     kfree(area);
  46.     return;
  47. }
  48. /**
  49.  *  remove_vm_area  -  find and remove a contingous kernel virtual area
  50.  *  @addr:      base address
  51.  *
  52.  *  Search for the kernel VM area starting at @addr, and remove it.
  53.  *  This function returns the found VM area, but using it is NOT safe
  54.  *  on SMP machines, except for its size or flags.
  55.  */
  56. struct vm_struct *remove_vm_area(void *addr)
  57. {
  58.     struct vm_struct *v;
  59.     write_lock(&vmlist_lock);
  60.     v = __remove_vm_area(addr);
  61.     write_unlock(&vmlist_lock);
  62.     return v;
  63. }
  64. /* Caller must hold vmlist_lock */
  65. static struct vm_struct *__remove_vm_area(void *addr)
  66. {
  67.     struct vm_struct **p, *tmp;
  68.     for (p = &vmlist ; (tmp = *p) != NULL ;p = &tmp->next) {
  69.          if (tmp->addr == addr)
  70.              goto found;
  71.     }
  72.     return NULL;
  73. found:
  74.     unmap_vm_area(tmp);
  75.     *p = tmp->next;
  76.     /*
  77.      * Remove the guard page.
  78.      */
  79.     tmp->size -= PAGE_SIZE;
  80.     return tmp;
  81. }

   这个函数也可以睡眠,因此不能在中断上下文中调用。

你可能感兴趣的:(内存管理[3])