在我的应用程序中,有一个进程将数据写入文件,然后在响应请求时,将其中的一部分数据通过网络发送给请求进程。这个问题的基础是看看当两个进程恰好在同一主机上时,我们是否可以加快通信速度。(在我的情况下,进程是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())或本地方法读取文件(通过“free”监视“cache”值),它都会这样做。
如果另一个进程尝试加载相同的文件(而该文件仍驻留在缓存中),内核将检测到此情况,并且不需要重新加载文件。如果页面缓存已满,则页面将被逐出-脏页面被写回到磁盘中。(如果显式刷新到磁盘,则还会将页面写回,并定期使用内核线程。)
将具有(大)文件的缓存是显着的性能提升,远远超过基于我们用于打开/读取该文件的Java方法的差异。
如果使用mmap系统调用(C)或通过FileChannel.map()(Java)加载文件,则文件的页面(在缓存中)直接加载到进程地址空间中。使用其他方法打开文件,文件加载到未在进程地址空间中的页面中,然后读/写该文件的各种方法将一些字节从/到这些页面复制到进程地址空间中的缓冲区中。避免该复制显然具有性能优势,但我的问题并不涉及性能。
因此,总之,如果我理解正确-虽然映射提供了性能优势,但似乎它并没有提供我们从Linux和页面缓存的性质中获得的任何“共享内存”功能。
请告诉我我的理解有哪些错误。
谢谢。
目前有一些项目使用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())或本地方法读取文件(通过“free”监视“cache”值),它都会这样做。
如果另一个进程尝试加载相同的文件(而该文件仍驻留在缓存中),内核将检测到此情况,并且不需要重新加载文件。如果页面缓存已满,则页面将被逐出-脏页面被写回到磁盘中。(如果显式刷新到磁盘,则还会将页面写回,并定期使用内核线程。)
将具有(大)文件的缓存是显着的性能提升,远远超过基于我们用于打开/读取该文件的Java方法的差异。
如果使用mmap系统调用(C)或通过FileChannel.map()(Java)加载文件,则文件的页面(在缓存中)直接加载到进程地址空间中。使用其他方法打开文件,文件加载到未在进程地址空间中的页面中,然后读/写该文件的各种方法将一些字节从/到这些页面复制到进程地址空间中的缓冲区中。避免该复制显然具有性能优势,但我的问题并不涉及性能。
因此,总之,如果我理解正确-虽然映射提供了性能优势,但似乎它并没有提供我们从Linux和页面缓存的性质中获得的任何“共享内存”功能。
请告诉我我的理解有哪些错误。
谢谢。