在内核中有效地分配内存

时间:2013-03-26 08:11:53

标签: c linux-kernel

我想编写一个内核模块,我将获得接近8 mbps的TCP / IP数据包。我必须将这些数据包存储500毫秒。稍后这些数据包应按顺序转发。这些应该为30个成员完成。应该采用哪种最佳方法?我应该kmalloc一次使用(kmalloc(64000000, GFP_ATOMIC)吗?因为如果我kmallockfree,每次都会花费时间,从而导致性能问题。另外,如果我一次性在内核中分配内存,linux内核会允许我这样做吗?

2 个答案:

答案 0 :(得分:4)

我曾经在10Gbs链接上编写了一个处理数据包的内核模块。我使用vmalloc分配大约1GB的连续(虚拟)内存来将静态大小的哈希表放入其中以执行连接跟踪(code)。

如果您知道需要多少内存,我建议您预先分配它。这有两个好处

  • 它很快(在运行时没有mallocing / freeing)
  • 如果kmalloc(_, GFP_ATOMIC)无法让你记忆,你不必考虑策略。这实际上经常在重负荷下发生。

缺点

  • 您可以根据需要分配更多内存。

因此,对于编写专用内核模块,请预先分配尽可能多的内存;)

如果为许多新手用户使用的商用硬件编写内核模块,那么按需分配内存(浪费更少的内存)会很不错。


你在哪里分配内存? GFP_ATOMIC只能返回非常少量的内存,只有在内存分配无法休眠时才能使用。您可以在安全睡眠时使用GFP_KERNEL,例如,不在中断环境中。有关详情,请参阅this question。在模块初始化期间使用vmalloc来预先分配所有内存是安全的。

答案 1 :(得分:0)

由于vmalloc,使用cornyanswer)时,像Linux kernel 5.2released Q3 2019中使用kernel changes会更快。

来自Michael Larabel

  

Linux内核的vmalloc代码具有在Linux 5.2上执行更快的潜力,尤其是在嵌入式设备上。
  Vmalloc用于在虚拟地址空间中分配连续的内存,并且在Linux 5.2合并窗口的预期最后一天今天进行了很好的优化合并。

     

几分钟前,与 Andrew Morton 合并的pull (commit cb6f873)的一部分是“对vmalloc进行了很大的更改,从而带来了巨大的性能优势。”

     

对vmalloc代码的主要更改是跟踪要分配的空闲块
  当前,新的VA区域的分配是通过繁忙列表迭代完成的,直到在两个繁忙区域之间找到合适的空洞为止。因此,每个新分配都会导致列表增长。由于清单较长且允许的参数不同,因此在嵌入式设备上进行分配可能需要很长时间(毫秒)。

     

此修补程序将vmalloc内存布局组织到VMALLOC_START-VMALLOC_END范围的空闲区域中。它使用一棵红黑树,使按偏移量对块进行排序,并与链表配对,从而按地址递增的顺序保留可用空间。

     

使用 Uladzislau Rezki 中的补丁,与Linux 5.1及更低版本上的行为相比,调用vmalloc()最多可以节省67%的时间,至少是由开发人员在QEMU。

提交,如mirrored on GitHub, is here

introduces a red-black tree

/*
 * This augment red-black tree represents the free vmap space.
 * All vmap_area objects in this tree are sorted by va->va_start
 * address. It is used for allocation and merging when a vmap
 * object is released.
 *
 * Each vmap_area node contains a maximum available free block
 * of its sub-tree, right or left. Therefore it is possible to
 * find a lowest match of free area.
 */

使用the function

/*
 * Merge de-allocated chunk of VA memory with previous
 * and next free blocks. If coalesce is not done a new
 * free area is inserted. If VA has been merged, it is
 * freed.
 */
static __always_inline void
merge_or_add_vmap_area(struct vmap_area *va,
    struct rb_root *root, struct list_head *head)

/*
 * Find a place in the tree where VA potentially will be
 * inserted, unless it is merged with its sibling/siblings.
 */

/*
 * Get next node of VA to check if merging can be done.
 */

/*
 * start            end
 * |                |
 * |<------VA------>|<-----Next----->|
 *                  |                |
 *                  start            end
 */
...
/*
 * start            end
 * |                |
 * |<-----Prev----->|<------VA------>|
 *                  |                |
 *                  start            end
 */