您的位置:首页 > 理论基础 > 数据结构算法

内存管理2get_vm_area

2012-05-06 08:18 435 查看

今天开始linux驱动27---内存管理2get_vm_area 

2010-06-04 16:53:03|  分类:

个人日记 |  标签:
|字号大中小
订阅

    周三

    struct vm_struct *get_vm_area(unsigned long size, unsigned long flags)

{

return __get_vm_area(size, flags, VMALLOC_START, VMALLOC_END);

}
    同样,是一层封装加了一些参数,起始地址和结束地址。其中起始地址用来找第一次对齐地址。结束地址作为越界标志。
我们先来看一个重要的数据结构,vm_struct

struct vm_struct {

void   *addr;//该内存块首地址

unsigned long  size;//该内存块的大小

unsigned long  flags;

struct page  **pages;

unsigned int  nr_pages;//页数

unsigned long  phys_addr;//映射的物理地址

struct vm_struct *next;//链表下一级地址

};

#define IOREMAP_MAX_ORDER (7 + PAGE_SHIFT) /* 128 pages */

struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags,

    unsigned long start, unsigned long end)

{

struct vm_struct **p, *tmp, *area;//申请内存块指针和2级指针做链表循环用

unsigned long align = 1;

unsigned long addr;

if (flags & VM_IOREMAP) {//如果是内存映射则按照申请大小对齐

  int bit = fls(size);//计算size中第一个非0的位的位数

  if (bit > IOREMAP_MAX_ORDER)//把大小定位在内存映射对齐地址范围之内

   bit = IOREMAP_MAX_ORDER;//page——shift到 page-shift+7吧

  else if (bit < PAGE_SHIFT)

   bit = PAGE_SHIFT;

  align = 1ul << bit;

}

addr = ALIGN(start, align);//按照内存映射大小取第一个可以对齐的地址,起始地址是start

area = kmalloc(sizeof(*area), GFP_KERNEL);//申请一个vm struct结构体大小内存,用作建立链表

if (unlikely(!area))

  return NULL;

/*

  * We always allocate a guard page.

  */

size += PAGE_SIZE;//申请内存的大小额外加一页的大小,用作越界处理的判断

if (unlikely(!size)) {

  kfree (area);

  return NULL;

}

write_lock(&vmlist_lock);

for (p = &vmlist; (tmp = *p) != NULL ;p = &tmp->next) {//循环寻找链表,直到找到足够的空闲内存

  if ((unsigned long)tmp->addr < addr) {//如果内存块起始地址小于对齐地址,则查找下一块

   if((unsigned long)tmp->addr + tmp->size >= addr)//如果对齐地址在该内存块内

    addr = ALIGN(tmp->size +

          (unsigned long)tmp->addr, align);//从该内存块结束地址开始重新对齐

   continue;

  }

  if ((size + addr) < addr)//翻转越界了

   goto out;

  if (size + addr <= (unsigned long)tmp->addr)//该内存块起始地址之前存在足够大的空闲内存

   goto found;

  addr = ALIGN(tmp->size + (unsigned long)tmp->addr, align);//否则空闲内存不够就重新对齐

  if (addr > end - size)//越界

   goto out;

}

found:

area->next = *p;//该空闲内存加入链表当中。临时也指向下一个

*p = area;//当前的next指向临时结构

area->flags = flags;//flags ioremap已经指定

area->addr = (void *)addr;//空闲内存首地址肯定是每次对齐最后留下的

area->size = size;//大小赋值

area->pages = NULL;

area->nr_pages = 0;

area->phys_addr = 0;

write_unlock(&vmlist_lock);

return area;

out:

write_unlock(&vmlist_lock);

kfree(area);

if (printk_ratelimit())

  printk(KERN_WARNING "allocation failed: out of vmalloc space - use vmalloc=<size> to increase size.\n");

return NULL;

}
//对应的释放某快映射内存的代码如下

/**

* remove_vm_area  -  find and remove a contingous kernel virtual area

*

* @addr:  base address

*

* Search for the kernel VM area starting at @addr, and remove it.

* This function returns the found VM area, but using it is NOT safe

* on SMP machines.

*/

struct vm_struct *remove_vm_area(void *addr)

{

struct vm_struct **p, *tmp;//仍然是临时结构体

write_lock(&vmlist_lock);

for (p = &vmlist ; (tmp = *p) != NULL ;p = &tmp->next) {//仍然是循环查找。二级指针指向next地址
//tmp指向下一块

   if (tmp->addr == addr)//直接找到地址

    goto found;

}

write_unlock(&vmlist_lock);

return NULL;

found:

unmap_vm_area(tmp);

*p = tmp->next;//上一块的地址指向下一块的地址,从链表上抽下来tmp

write_unlock(&vmlist_lock);

return tmp;

}
//umap的代码

void unmap_vm_area(struct vm_struct *area)

{

unsigned long address = (unsigned long) area->addr;

unsigned long end = (address + area->size);

pgd_t *dir;

dir = pgd_offset_k(address);

flush_cache_vunmap(address, end);

do {

  unmap_area_pmd(dir, address, end - address);

  address = (address + PGDIR_SIZE) & PGDIR_MASK;

  dir++;

} while (address && (address < end));

flush_tlb_kernel_range((unsigned long) area->addr, end);

}

这里的内存池的代码很干练,值得多看看,只用了一个链表就能把空闲和使用的内存统统记录,虽然链表本身记录的是使用的内存,但是未使用的内存就自动标记并可以合并了很好很强大
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息