18

我正在编写一个程序,它从网络接收大量数据(大小不同),处理它们并将它们写入内存。由于某些数据可能非常大,我目前的方法是限制使用的缓冲区大小。如果一块大于最大缓冲区大小,我将数据写入临时文件,然后以块的形式读取文件以进行处理和永久存储。

我想知道这是否可以改进。我一直在阅读有关 mmap 的文章,但我不能百分百确定它是否可以帮助我。我的想法是使用 mmap 来读取临时文件。这有什么帮助吗?我担心的主要事情是偶尔的大块数据不应该填满我的主内存,导致其他所有内容都被换出。

另外,您认为使用临时文件的方法有用吗?我应该这样做,还是应该相信 linux 内存管理器为我完成这项工作?或者我应该完全做其他事情吗?

4

3 回答 3

16

Mmap 可以在某些方面为您提供帮助,我将通过一些假设示例进行解释:

第一件事:假设您的内存不足,并且您的应用程序拥有 100MB 的 malloc 内存块,其中 50% 被换出,这意味着操作系统必须将 50MB 写入交换文件,如果您需要读回它,你已经写入、占用然后再读回 50MB 的交换文件。

如果内存刚刚被映射,操作系统不会将该信息写入交换文件(因为它知道该数据与文件本身相同),相反,它只会刮掉 50MB 的信息(再次:假设你现在还没有写任何东西),就是这样。如果您需要再次读取该内存,操作系统将不会从交换文件中获取内容,而是从您映射的原始文件中获取内容,因此如果任何其他程序需要 50MB 的交换,它们是可用的。交换文件操作也没有任何开销。

假设你读取了一个 100MB 的数据块,根据最初的 1MB 头数据,你想要的信息位于偏移量 75MB,所以你不需要 1~74.9MB 之间的任何东西!您阅读它只是为了让您的代码更简单。使用 mmap,您将只读取您实际访问过的数据(四舍五入 4kb,或操作系统页面大小,大部分为 4kb),因此它只会读取第一个和第 75 个 MB。我认为很难找到一种比映射文件更简单有效的方法来避免磁盘读取。如果由于某种原因您需要偏移 37MB 的数据,您可以使用它!您不必再次对其进行 mmap,因为整个文件都可以在内存中访问(当然受您进程的内存空间的限制)。

所有mmap'ed的文件都是自己备份的,而不是swapfile,swapfile用于授予没有文件要备份的数据,通常是malloc'ed数据或由文件备份的数据, 但在程序通过 msync 调用实际告诉操作系统这样做之前,它已被更改并且 [不能/不应] 被写回它。

请注意,您不需要将整个文件映射到内存中,您可以从任何位置(第 6 个参数 - “off_t 偏移量”)开始映射任意数量(第二个参数是“size_t 长度”),但除非您的文件很可能要变得巨大,即使系统仅包含 64mb 的物理内存,您也可以安全地映射 1GB 的数据,但那是用于读取的,如果您打算写入,那么您应该更加保守,只映射您需要的东西.

映射文件将帮助您使您的代码更简单(您已经在内存中拥有文件内容,可以使用,由于它不是匿名内存,因此内存开销要少得多)并且更快(您将只读取程序访问的数据)。

于 2012-04-25T02:14:55.743 回答
2

使用大文件进行 mmap 的主要优点是在两个或多个文件之间共享相同的内存映射:如果您使用 mmap 进行映射MAP_SHARED,则对于将使用数据的所有进程,它将仅加载到内存中一次,并节省内存。

但是 AFAIK , mmap 将整个文件映射到内存中(在这里您可以找到 mmap 如何因文件大于物理内存 + 交换空间而失败的示例。)因此,如果您从单个进程访问该文件,它不会帮助您处理物理内存消耗。

于 2012-04-24T18:48:09.010 回答
1

我相信 mmap 不需要所有数据同时在内存中 - 它使用页面缓存将最近使用的页面保存在内存中,其余的则保存在磁盘上。

如果您一次读取一个块,使用临时文件可能对您没有帮助,但如果您正在使用多个线程、进程或使用 select/poll 同时读取多个块,那么它可能会。

于 2012-04-24T22:49:09.883 回答