获取 FileChannel 的 Linux 个文件描述符

Get Linux file descriptor of FileChannel

有没有办法为打开的 FileChannel 获取 Linux 文件描述符?

我需要它来调用 mount.fuse -o fd=...(用于实现 FUSE)。

作为一个 hacky 解决方法,我正在做:

var pid = ProcessHandle.current().pid();
var fd = Files.list(Path.of("/proc/"+pid+"/fd")).count();
var fc = FileChannel.open(path);
System.out.println("file descriptor: " + fd);

请注意,出现了两个文件描述符。一个用于 path,另一个用于 socket。我用的是第一个。插座有什么用?

您可以使用反射从 RandomAccessFile:

中获取文件描述符
long pid = ProcessHandle.current().pid();
long guessedFd = Files.list(Path.of("/proc/"+pid+"/fd")).count();

var file = new java.io.RandomAccessFile(FUSE_DEVICE_PATH.toFile(), "rw");
FileChannel fc = file.getChannel(); // Use FileChannel for fast NIO
var javaFd = file.getFD();
try {
    Field f = FileDescriptor.class.getDeclaredField("fd");
    f.setAccessible(true);
    var trueFd = (int) f.get(javaFd);
    return trueFd;
}
catch (InaccessibleObjectException | NoSuchFieldException | IllegalAccessException e) {
    return guessedFd;
}

在 Java 11+ 上,您将在启动时选择 jvm 选项 --add-opens=java.base/java.io=ALL-UNNAMED。在 Java 18.

上测试

在 Java 17 中你可以使用 SharedSecrets 而不是反射:

FileDescriptor javaFd = ...
try {
    int trueFd = jdk.internal.access.SharedSecrets.getJavaIOFileDescriptorAccess().get(javaFd);
    return trueFd;
}
catch (IllegalAccessError e) {
    return guessedFd;
}

编译时需要加上--add-exports=java.base/jdk.internal.access=ALL-UNNAMED和运行。参见 for Maven