vmalloc浅析

vmalloc的流程比较简单理解起来没什么难度,大致分为1.为vm管理数据结构分配空间,包括vm_struct和vmap_area 2.根据size申请合适的物理内存 3.修改页表条目。这篇blog就简单的记录下vmalloc的实现流程,以及部分容易被误导的地方(如vmalloc真的全部都来自于high memory吗?)。

先提几个问题:

  • vmalloc分配的物理内存会存在浪费吗?
  • vmalloc分配的各size之间必须加4K(a page)的gap吗?

先看函数流程再回头研究数据结构。
vmalloc->__vmalloc_node_flags->__vmalloc_node->__vmalloc_node_range
从代码来看vmalloc内部的具体实现在__vmalloc_node_range()函数。

	1652 void *__vmalloc_node_range(unsigned long size, unsigned long align,
	1653             unsigned long start, unsigned long end, gfp_t gfp_mask,
	1654             pgprot_t prot, unsigned long vm_flags, int node,
	1655             const void *caller)
	1656 {  
	1657     struct vm_struct *area;
	1658     void *addr;
	1659     unsigned long real_size = size;
	1660    
	1661     size = PAGE_ALIGN(size);
	1662     if (!size || (size >> PAGE_SHIFT) > totalram_pages)
	1663         goto fail;
	1665     area = __get_vm_area_node(size, align, VM_ALLOC | VM_UNINITIALIZED |
	1666                 vm_flags, start, end, node, gfp_mask, caller);
	1667     if (!area)
	1668         goto fail;
	1669    
	1670     addr = __vmalloc_area_node(area, gfp_mask, prot, node);
	1671     if (!addr)
	1672         return NULL;
	1673    
	1674     /*
	1675      * In this function, newly allocated vm_struct has VM_UNINITIALIZED
	1676      * flag. It means that vm_struct is not fully initialized.
	1677      * Now, it is fully initialized, so remove this flag here.
	1678      */
	1679     clear_vm_uninitialized_flag(area);
	1680    
	1681     /*
	1682      * A ref_count = 2 is needed because vm_struct allocated in
	1683      * __get_vm_area_node() contains a reference to the virtual address of
	1684      * the vmalloc'ed block.
	1685      */
	1686     kmemleak_alloc(addr, real_size, 2, gfp_mask);
	1687    
	1688     return addr;
	1689    
	1690 fail:
	1691     warn_alloc_failed(gfp_mask, 0,
	1692               "vmalloc: allocation failure: %lu bytes\n",
	1693               real_size);
	1694     return NULL;
	1695 }

这里比较重要的函数就是__get_vm_area_node和__vmalloc_area_node。
首先是__get_vm_are_node, 用于完成vmalloc管理数据结构vm_struct和vmap_area的内存分配和初始化。这两个管理数据结构都是通过kmalloc从低端内存申请的。vmap_area还需要找到能用的起始虚拟地址。内核为了加快搜索过程通过rb tree来管理之前已经分配的出去的vmap_area,通过之前rb能快速计算出剩余VA中能满足要求的虚拟地址块。算完之后初始化到vmap_area数据结构中。
这里需要注意的有两点。
1.vmalloc浪费问题。从1340行可见,申请的size会直接进行页对齐。也就是说如果size,哪怕多出一个Byte,vmalloc也会去多申请一个整个page的物理内存,这就存在了浪费问题。

1340     size = PAGE_ALIGN(size); 

2.经常有文章或者博客说,每个vm之间会使用一个page来隔开。但是通过代码可知,至少v4.1.50的内核版本中这个特性已经变成可配置的了,如果有VM_NO_GUARD宏内核是不再会添加4KB的gap来隔开。

1344     area = kzalloc_node(sizeof(*area), gfp_mask & GFP_RECLAIM_MASK, node);
1345     if (unlikely(!area))
1346         return NULL;
1347                  
1348     if (!(flags & VM_NO_GUARD))
1349         size += PAGE_SIZE;
1350                  
1351     va = alloc_vmap_area(size, align, start, end, node, gfp_mask);  

然后是_vmalloc_are_node,这里主要就是根据vmap_area中的size去申请物理内存,最少都会申请的一个物理page,然后修改对应的一级页表。

可以知道vmalloc修改的页表项是属于内核部分的,且物理内存已经分配好,并不会存在page fault的问题。

另外高端内存可以看到会有flag,__GFP_HIGHMEM的flag,可以搜索内核查看由哪些地方会从high mem处申请内存。

猜你喜欢

转载自blog.csdn.net/rockrockwu/article/details/80481356