内存管理2get_vm_area

今天开始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);
}
这里的内存池的代码很干练,值得多看看,只用了一个链表就能把空闲和使用的内存统统记录,虽然链表本身记录的是使用的内存,但是未使用的内存就自动标记并可以合并了很好很强大
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值