vmalloc 和 kmalloc 的在逻辑地址上时连续的,他们的区别在于 vmalloc 在物理地址上并不时连续的,但是 kmalloc 在物理地址上页时连续的。 Vmalloc 分配分为两种模式,一种是没有 MMU(memory management unit, 内存管理模块 ) 的 vmalloc ,比如在 arm7 上,另一种则是在有 MMU 上的 vmalloc 。
在没有 MMU 模块支持的 cpu 上, vmalloc 则是调用的 slab 中的 kmalloc 实现的内存分配,其标志为 (gfp_mask | __GFP_COMP) & ~__GFP_HIGHMEM
在有 MMU 模块的体系架构中的 vmalloc() ,则是通过 __vmalloc_node() 来分配连续逻辑内存
__vmalloc_node(unsigned long size, gfp_t gfp_mask, pgprot_t prot,int node, void *caller) 的基本流程如下:
1、 错误处理,当待分配的大小为 0 ,或者将 size/ 页面大小 大于 num_physpages 时,返回空
2、 尝试调用 __get_vm_area_node() 获得虚拟内存空间的节点,失败则返回 null
3、 返回 __vmalloc_area_node(area, gfp_mask, prot, node, caller); 的返回值
__vmalloc_area_node(area, gfp_mask, prot, node, caller)
1、 把 nr_pages 设置成 vmalloc 需要的线形地址的页面数量
2、 设置 array_size 成线形地址的页面所需要的指针空间的大小
3、 将 nr_page 交给 area->nr_pages
4、 当 array_size 大于页面大小时,调用 __vmalloc_node(array_size, gfp_mask | __GFP_ZERO,PAGE_KERNEL, node, caller) 分配空间给 area->pages 。否则,将 kmalloc_node(array_size,(gfp_mask & GFP_RECLAIM_MASK) | __GFP_ZERO,node) 赋值给 area->pages
5、 把 caller 的值传给 area->caller
6、 如果 area->pages 分配时,移除 area->addr 中的地址,并且释放 area ,返回 null
7、 遍历刚分配的页面,当 node 为负数时,采用 alloc_page(gfp_mask) 为 page 分配空间,或者,则利用 alloc_pages_node(node, gfp_mask, 0) 。分配失败时,将 area->nr_pages 的值设置成 i ,释放掉 area->addr 中地址的使用权,返回 null. 将 area_page[i] 设置成刚申请成功的内存页面。当 map_vm_area(area, prot, &pages) 成功映射,返回 area->addr ,否则释放掉 area_addr ,并返回空值。
在第七步可以知道, vmalloc 在对内存进行分配的时候不是“慷慨”的一次性将内存给分配完,而是采用一个循环的方式,多次调用 alloc_page() 或者 alloc_page_node 对内存进行分配。这样或许可以更加充分的利用内存,一些零碎的小内存单元可能被利用,但是在速度上就相对于 kmalloc 的速度要慢不少。 Kmalloc 针对内存紧张的小内存单元页有相应的 slob 的分配方式,甚至在一些不支持 MMU 的设备上 vmalloc 等同于 kmalloc 。所以我个人认为在实际内核编码过程中,更多的使用 kmalloc 会好些。