【问题标题】:How to memory map (mmap) a linux block device (e.g. /dev/sdb) in Java?如何在 Java 中内存映射(mmap)Linux 块设备(例如 /dev/sdb)?
【发布时间】:2013-03-31 21:02:31
【问题描述】:

我可以使用java.nio 用 Ja​​va 读/写一个 linux 块设备。以下代码有效:

Path fp = FileSystems.getDefault().getPath("/dev", "sdb");
FileChannel fc = null;
try {
  fc = FileChannel.open(fp, EnumSet.of(StandardOpenOption.READ, StandardOpenOption.WRITE));
} catch (Exception e) {
  System.out.println("Error opening file: " + e.getMessage());
}
ByteBuffer buf = ByteBuffer.allocate(50);
try {
  if(fc != null)
    fc.write(buf);
} catch (Exception e) {
  System.out.println("Error writing to file: " + e.getMessage());
}

但是,内存映射不起作用。以下代码失败

MappedByteBuffer mbb = null;
try {
  mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 100);
} catch (IOException e) {
  System.out.println("Error mapping file: " + e.getMessage());
}

这失败并出现错误:

java.io.IOException: Invalid argument
    at sun.nio.ch.FileDispatcherImpl.truncate0(Native Method)
    at sun.nio.ch.FileDispatcherImpl.truncate(FileDispatcherImpl.java:79)
    at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:817)

有没有办法解决这个问题?也许通过使用不同的库?我在某处读到,也许通过使用 JNI 我可以做到这一点,但我找不到任何来源。

【问题讨论】:

  • 那是你的真实代码吗?当然 truncate() 只在只写模式下调用?

标签: java java-native-interface mmap memory-mapping


【解决方案1】:

根据documentation,实际映射文件的机制留给实现。似乎该实现正在尝试截断文件(可能是因为块设备大小与您指定的大小不同?)。

我很好奇您为什么要直接从块设备中读取数据(除非您尝试编写某种文件系统实用程序或需要执行原始 I/O 的东西)。如果您需要直接从块设备作为内存映射文件读取,您可能需要编写一些 C/C++ 代码来映射文件并处理对其的读/写,并使用 Java/JNI 桥接类来桥接对您的调用C/C++ 代码。这样您就可以自己处理调用 mmap() 并且可以指定您需要的任何选项。查看mmap() documentation,您可能无法在您的平台上指定块设备(我猜是 Linux,但我可能是错的)。

如果您绝对需要在 Java 中执行此操作,您可能需要执行适当长度和偏移量的 read() 调用和 write() 调用。

【讨论】:

  • 这就是为什么不能将 Linux 帧缓冲区与 stock 类进行映射的原因。但这是可以完成的。你必须实现一些东西才能到达那里。
【解决方案2】:

我发现最简单的方法是使用JNA 和一点sun.*/com.sun.* 魔法。首先你需要像这样包装libc

import com.sun.jna.LastErrorException;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;

public interface LibC extends Library {
    LibC instance = (LibC) Native.loadLibrary("c", LibC.class);

    Pointer mmap(Pointer address, long length, 
                 int protect, int flags, int fd, 
                 long offset) throws LastErrorException;
    // implement more methods if you like
}

然后你就差不多完成了!您只需要获取文件描述符。这可能有点棘手:

RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
int fd = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess()
                               .get(randomAccessFile.getFD());

就是这样。现在你可以从 java 调用libc

Pointer result = LibC.instance.mmap(
    /*pass your desired parameters along with obtained fd*/
);

【讨论】:

    【解决方案3】:

    FileChannel.map 尝试将文件截断为指定大小:See the implementation

    您需要获取块设备的大小并将该确切大小传递给 map 调用。

    如果实际设备的大小大于您的可用内存,请不要担心。操作系统将根据需要处理内存交换页面。

    【讨论】:

      猜你喜欢
      • 2011-03-30
      • 1970-01-01
      • 2014-05-24
      • 2012-05-07
      • 1970-01-01
      • 2019-07-08
      • 1970-01-01
      • 1970-01-01
      • 2016-03-27
      相关资源
      最近更新 更多