vmalloc函数实现细节

对于x86体系[luther.gliethttp]
#define high_memory (-128UL << 20)等于0xf8000000

#define VMALLOC_OFFSET    (8 * 1024 * 1024)
#define VMALLOC_START    ((unsigned long)high_memory + VMALLOC_OFFSET)
#define VMALLOC_END        (PKMAP_BASE - 2 * PAGE_SIZE)

#define PKMAP_BASE ((FIXADDR_BOOT_START - PAGE_SIZE * (LAST_PKMAP + 1))    /
            & PMD_MASK)

即vmalloc的起始地址为0xf8800000

---------------------------------------------------------------
以下转自:http://www.360doc.com/content/09/0525/21/36491_3650302.shtml

高端物理地址的分配 采用 vmalloc/vfree 这组函数进行,什么是高端物理内存呢?我们知道 Linux 给内核预留了一部分虚拟地址空间,这部分虚拟地址如果能全部直接映射到物理地址空间就不存在高端内 存。如果这部分内存有一部分不能直接映射到地址空间,那么这部分虚拟地址空间称为高端内存。因此,高端内存是虚拟地址空间中的概念。举个例子:如果你的物 理内存为 512M , 那么就不存在高端内存的分配,如果你的物理地址为 2G ,那么有 1G+128M (预留给 VMALLOC 区)是属于高端内存的。高端内存的分配即便是逻辑上连续,也不要求物理上是连续的。

       下面我们介绍一下高端内存的分配。分配高端内存是通过调用 vmalloc 函数。我们来解读一下该函数的 分配过程。

首先介绍一下这部分内存管理所需要 的数据结构:

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; // 单链表,指向下一个 vm 节点

};

 

1.          首先检查请求分配的内存大小有没有超过最大的物理页面数。如果超过返回 0 ,表示分配失败。

       size = PAGE_ALIGN(size);

       if (!size || (size >> PAGE_SHIFT) > num_physpages)

              return NULL;

2.          使用 kmalloc slab 中,分配 vm_struct 数据结构。

       area = kmalloc_node(sizeof(*area), GFP_KERNEL, node);

3.          在单链表 vmlist 中查找适合的位置,并将新的 vm 节点插入到单链表中。

       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; // 地址越界

       }

       // 插入新的 vm 节点到 vmlist 中去。

       area->next = *p;

       *p = area;

       // 初始化新结点

       area->flags = flags;

       area->addr = (void *)addr;

       area->size = size;

       // 物理内存还未分配。

       area->pages = NULL;

       area->nr_pages = 0;

       area->phys_addr = 0;

 

4.          接下来初始化 vm_struct 结构中的 pages nr_pages 字段。

a)          初始化 nr_pages 字段

              nr_pages = (area->size - PAGE_SIZE) >> PAGE_SHIFT;

b)         初始化 pages 数组

计算数组大小

       array_size = (nr_pages * sizeof(struct page *));

如果数组大小大于 1 个页面,在非连续区进行分配,否则在连续区 进行分配

       if (array_size > PAGE_SIZE)

              pages = __vmalloc_node(array_size, gfp_mask, PAGE_KERNEL, node);

       else

              pages = kmalloc_node(array_size, (gfp_mask & ~__GFP_HIGHMEM), node);

area->pages = pages;

 

5.          从伙伴系统中进行物理内存页面的分配

       for (i = 0; i < area->nr_pages; i++) {

              if (node < 0)

                     area->pages[i] = alloc_page(gfp_mask); // 针对 UMA

              else

                     area->pages[i] = alloc_pages_node(node, gfp_mask, 0); // 针对 NUMA

              if (unlikely(!area->pages[i])) {

                     /* Successfully allocated i pages, free them in __vunmap() */

                     area->nr_pages = i;

                     goto fail;

              }

       }

 

6.    将刚申请的页面映射到页表中。

        if (map_vm_area(area, prot, &pages))           

 

释放内存部分很简单,就是从 vmlist 当中删除掉对应的节点。然后将 内存归还给伙伴系统

你可能感兴趣的:(vmalloc函数实现细节)