【发布时间】:2020-09-09 17:30:43
【问题描述】:
在我的应用程序中,有一个进程将数据写入文件,然后响应接收请求,将通过网络将(部分)数据发送到请求进程。这个问题的基础是看当两个进程碰巧在同一个主机上时,我们是否可以加快通信速度。 (在我的例子中,流程是 Java,但我认为这个讨论可以更广泛地应用。)
有一些项目使用 Java 的 FileChannel.map() 返回的 MappedByteBuffers 作为在同一主机上的 JVM 之间共享内存 IPC 的一种方式(请参阅 Chronicle Queue、Aeron IPC 等)。
加速同一主机通信的一种方法是让我的应用程序使用其中一种技术来为同一主机通信提供请求-响应路径,或者结合现有的写入数据文件的机制,或者通过提供统一的通信和写入文件的方式。
另一种方法是允许请求进程直接访问数据文件。
我倾向于支持第二种方法 - 假设它是正确的 - 因为它更容易实现,并且似乎比为每个请求复制/传输数据副本更有效(假设我们没有替换现有的写入文件的机制)。
基本上,我想了解当两个进程可以访问同一个文件并使用它进行通信时究竟会发生什么,特别是 Java (1.8) 和 Linux (3.10)。
根据我的理解,如果两个进程同时打开同一个文件,那么它们之间的“通信”本质上将是通过“共享内存”。
请注意,此问题与是否使用 MappedByteBuffer 的性能影响无关 - 与读取和写入文件相比,使用映射缓冲区以及减少复制和系统调用似乎很有可能会减少开销,但这可能需要对应用程序进行重大更改。
这是我的理解:
- 当 Linux 从磁盘加载文件时,它会将该文件的内容复制到内存中的页面。该内存区域称为页面缓存。据我所知,无论使用哪种 Java 方法(FileInputStream.read()、RandomAccessFile.read()、FileChannel.read()、FileChannel.map())或本机方法来读取文件,它都会执行此操作(观察“免费”并监控“缓存”值)。
- 如果另一个进程尝试加载相同的文件(当它仍然驻留在缓存中时),内核会检测到这一点并且不需要重新加载文件。如果页面缓存已满,页面将被逐出——脏页面被写回磁盘。 (如果有明确的磁盘刷新,页面也会被写回,并且会定期使用内核线程)。
- 在缓存中已经有一个(大)文件可以显着提升性能,这比我们使用哪些 Java 方法打开/读取该文件的差异要大得多。
- 如果使用 mmap 系统调用 (C) 或通过 FileChannel.map() (Java) 加载文件,则基本上文件的页面(在缓存中)被直接加载到进程的地址空间中。使用其他方法打开文件,将文件加载到不在进程地址空间中的页面中,然后读取/写入该文件的各种方法将一些字节从/到这些页面复制到进程地址空间中的缓冲区中.避免该副本有明显的性能优势,但我的问题与性能无关。
总之,如果我理解正确的话——虽然映射提供了性能优势,但它似乎没有提供任何“共享内存”功能,而这些功能我们还没有从 Linux 和页面的性质中获得缓存。
所以,请让我知道我的理解在哪里。
谢谢。
【问题讨论】:
-
与 java 问题相比,将其作为 linux 问题发布可能更幸运。
-
@Deadron - 已采纳建议。我将 Linux 添加为标签。谢谢。
标签: java linux ipc memory-mapped-files mappedbytebuffer