如何用Java存储器映射(MMAP)一个Linux块设备(例如/ dev / SDB)?(How t

2019-08-20 23:07发布

我可以读/写使用linux的块设备使用Java 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我能做到这一点,但我找不到任何来源。

Answer 1:

根据该文件的实际映射文件的机制都留给了实现。 看来,实现正试图截断文件(或许是因为块设备的大小是不一样的,你指定的大小?)。

我很好奇,为什么你是直接从块设备读取(除非你正在尝试写一些类型的文件系统实用程序或一些需要做的原始I / O的)。 如果你需要从块设备的内存映射文件直接读取,您可能需要编写一些C / C ++代码映射文件和处理的读/写,并使用Java / JNI桥类桥接到呼叫您C / C ++代码。 这样,你处理调用mmap()的自己,也可以指定所需的任何选项。 纵观mmap()的文档,你可能无法达到你的平台上指定块设备(我猜Linux,但我可能是错的)。

如果你确实需要在Java中做到这一点,你可能需要做阅读()调用和write()适当的长度和偏移的电话。



Answer 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());

而已。 现在,你可以调用libc从Java:

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


Answer 3:

FileChannel.map尝试将文件截断到指定的大小: 见实施

您将需要获得块设备的大小和精确的尺寸传递给地图调用。

如果实际设备的大小比可用内存大别担心。 根据需要OS将处理在交换和内存页。



文章来源: How to memory map (mmap) a linux block device (e.g. /dev/sdb) in Java?