不使用交换分区分配最大缓冲区

13
在Linux的C/C++中,我需要分配一个大块(几个GB)的内存,以存储通过以太网端口连接并以约110MB/s流式传输数据的传感器实时数据。我想尽可能地分配最大量的内存,以最大化可以存储的数据序列的长度。但是,我还需要确保不会发生磁盘交换,因为磁盘访问的延迟和带宽受限会导致传感器(非常有限的)缓冲区溢出。
如何确定分配多少内存是最好的方式?我只能分配比报告的可用内存略小的块,还是可以更直接地与Linux虚拟内存管理器进行交互?

纯属出于兴趣,你正在使用什么传感器? - Konrad
重复的问题:https://dev59.com/HnE85IYBdhLWcg3w_I38 - Björn Pollex
@Space_C0wb0y:这并不是真的。 - Hasturkun
获取更好的传感器。或者切换到适当的实时操作系统。虚拟内存操作系统不会让你做想做的事情,除非你像使用RAM磁盘作为交换空间这样做一些奇怪的事情。我曾经看到过IDE接口另一侧的4GB+ RAM。适当供电的RAM。非常快。对于颠覆基于虚拟内存的操作系统并使其全部缓存在RAM中非常有用。 - Chris Becke
1
@Chris Becke:Linux并不强制要求你做任何奇怪的事情来使缓冲区无法交换;有一个直接的系统调用可以将它们锁定到物理内存中,或者如果你想的话,可以配置系统根本没有交换磁盘。我发现它的实时行为对于几毫秒或更长时间的实时约束是相当令人满意的。 - Mike Seymour
@Konrad:这是一款Dalsa Genie HM1400相机。 @Space_C0wb0y:感谢提供相关问题的链接。 @Chris Becke:传统解决此问题的方式是使用具有您建议的嵌入式实时操作系统和大量RAM的硬件单元。不过超出了我的预算... - Chris Johnson
4个回答

10

在 Linux 下,您可以使用 mlock() / mlockall() 将某个地址范围保留在物理内存中,防止其被交换出去。使用 mlock 的进程需要一些特权才能这样做,“man mlock” 中有详细说明。我不确定最大的可锁定块大小(它可能与“free”所显示的不同),所以可能需要进行二分查找(锁定一段范围,如果失败则减小区域的大小等)。

另一方面,110MB/s 对于固态硬盘来说并不是真正的问题。一个写入速度为 280MB/s 的 60GB 固态硬盘在市面上只需大约200美元。只需将传感器数据复制到一个小写缓冲区中,并将其流式传输到固态硬盘即可。


3
如果计算机系统专门用于接收传感器数据,您可以简单地禁用交换区。然后分配尽可能大的缓冲区,在系统中留下足够的内存仅用于必要工具。

很遗憾,它不是一个专用系统。然而,它有足够的内存供其他用途使用,因此禁用交换空间可能是一个选择。 - Chris Johnson
检查一段时间的内存使用情况。你可能会发现,除非某些应用程序开始疯狂泄漏内存,否则几乎没有使用交换空间。我一直在运行没有交换空间的桌面系统,没有任何问题。 但是为了更普遍的解决方案,请像路德建议的那样使用mlock(2)。不要将所有内容都分配为一个连续缓冲区。内存管理器可能无法分配(或重新分配)它。使用许多较小的缓冲区通过某个接口连接起来。这样,您可以在运行时调整缓冲区大小。 - Tomek Szpakowicz
"buffor" 拼写赞一个 :P! - Cray

0
如果您使用malloc分配所需的内存并以该速度写入,则仍会出现性能下降,原因是由于所有页面故障(即将虚拟内存的每个页面映射到物理内存,这也可能包括交换其他进程的内存)。

为了避免这种情况,您可以在从传感器开始读取之前将整个分配的缓冲区memset为0,以便将所有所需的虚拟内存映射到物理内存。
如果您只使用可用的物理内存,则不应发生任何交换。使用更多会导致其他进程的内存被交换到磁盘上-如果这些进程处于空闲状态,则不应造成任何问题。如果它们处于活动状态(即偶尔使用它们的内存),则会发生一些交换-可能比硬盘带宽低得多。您使用的内存越多,交换出更多活动进程的内存,并且会发生更多的硬盘活动-此时您可以使用的最大内存量基本上是试错的结果。
通过使用超过可用物理内存的内存,您肯定会导致内存写入速率的交换,并且没有办法避免这种情况。

2
不需要使用 memset() 来欺骗系统分配物理内存。使用 mlock() 显式地进行操作,并确保它永远不会被交换出去。 - Mike Seymour

0
什么是确定分配多少内存的最佳方法?
由于虚拟内存的使用方式,不可交换的内核内存,几乎无法确定应用程序可以访问多少已安装内存。
我能想到的最好的方法是允许用户配置要用于缓冲的内存量。
我只能分配比报告的空闲内存稍小的块吗?
报告的空闲内存实际上并非“自由物理内存”。很遗憾。
还是我可以更直接地与Linux虚拟内存管理器进行接口?
这可以通过使用自定义设备驱动程序实现,直接在内核空间中分配内存并通过mmap()提供对其的访问来完成。一般不建议使用,但会在像你这样的特定情况下起作用。
然而,我还需要确保不会有磁盘交换
随着Linux内核开发的步伐,知识很快就会过时,所以接受我在这里说的话时要有点保留。您可以尝试玩以下内容:
1. SysV共享内存。通常不会被交换出去。请参考man shmget
2. tmpfs - 内存文件系统。至少在早期的2.6内核中,内存被固定在RAM中,因此不可交换。要将其用作内存,请在tmpfs上创建一个文件,write()一些内容到文件中(以强制实际分配内存),然后使用mmap()映射该文件。

仅供记录,tmpfs 实际上是可交换的。还有另一种变体 rootfs 永远不会被交换。但正因为如此,您必须非常小心,并且不应过度使用它。 - Jens Gustedt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接