8

我可以使用 Java 读/写 linux 块设备java.nio。以下代码有效:

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 我可以做到这一点,但我找不到任何来源。

4

3 回答 3

3

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

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

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

于 2013-07-10T02:50:37.080 回答
3

我发现最简单的方法是使用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());

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

Pointer result = LibC.instance.mmap(
    /*pass your desired parameters along with obtained fd*/
);
于 2016-02-26T22:57:00.363 回答
2

FileChannel.map 尝试将文件截断为指定大小:请参阅实现

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

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

于 2013-09-19T21:01:25.747 回答