我想编写一个内核模块,我可以在其中获得接近 8 mbps 的 TCP/IP 数据包。我必须将这些数据包存储 500 毫秒。稍后这些数据包应按顺序转发。这些应该为 30 名成员完成。最好的实施方法应该是什么?我应该使用kmalloc
一次(kmalloc(64000000, GFP_ATOMIC)
吗?因为每次如果我这样做kmalloc
,kfree
都需要时间,导致性能问题。另外,如果我一次性在内核中分配内存,linux内核会允许我这样做吗?
2 回答
我曾经写过一个内核模块处理 10Gbs 链路上的数据包。我曾经vmalloc
分配大约 1GByte 的连续(虚拟)内存来将静态大小的哈希表放入其中以执行连接跟踪(代码)。
如果你知道你需要多少内存,我建议预先分配它。这有两个好处
- 它很快(在运行时没有 mallocing/freeing)
kmalloc(_, GFP_ATOMIC)
如果不能恢复记忆,你不必想一个策略。这实际上可能在重负载下经常发生。
坏处
- 您可能需要分配更多的内存。
所以,为了编写一个特殊用途的内核模块,请预先分配尽可能多的内存;)
如果您为许多新手用户使用的商品硬件编写内核模块,那么按需分配内存(并且浪费更少的内存)会很好。
你在哪里分配内存?GFP_ATOMIC
只能返回非常少量的内存,并且只应在您的内存分配无法休眠时使用。您可以GFP_KERNEL
在可以安全睡眠时使用,例如,不在中断上下文中。有关更多信息,请参阅此问题。vmalloc
在模块初始化期间使用预分配所有内存是安全的。
由于内核更改,在Linux 内核 5.2(2019 年第三季度发布vmalloc
)中使用老生常谈的答案 会更快。
来自迈克尔·拉拉贝尔:
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 下完成的测试中是这样。
提交,如GitHub 上的镜像,在这里:
它引入了一个红黑树:
/*
* 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.
*/
具有以下功能:
/*
* 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
*/