使用Java中的编号文件描述符

24

我需要从Java访问带编号的文件描述符,除了0、1或2以外。

如何实现?我查看了FileDescriptor类,但没有找到任何用给定文件描述符号码进行初始化的方法。

以具体的例子来说,假设Java作为另一种编程语言的子进程被调用。其他语言提供输入和输出的文件描述符3和4。

我在Java中需要连接到这些文件描述符的InputStreamOutputStream对象,就像System.in、System.out和System.error连接到文件描述符0、1和2一样。

我正在使用Java 1.6,并且应该在类Unix系统上运行。


2
http://www.kfu.com/~nsayer/Java/jni-filedesc.html 可能会有所帮助。 - user180100
你的工作解决方案应该在答案中,而不是问题的一部分。 - Charles Duffy
5个回答

19

我很确定这不能使用纯Java完成,你可能需要使用本地代码将文件描述符绑定到FileDescriptor对象或FileInputStream或FileOutputStream对象。

编辑
如果你在使用Linux、*BSD或macOS操作系统,你可以使用伪文件/dev/fd/nnn来访问文件描述符nnn。


1
是的,当从您的答案中了解到Java本身不支持此功能时(我没有想到),我开始研究操作系统对其的支持。我正在FreeBSD下运行,并且对于所有打开的文件描述符都有/dev/fd/<nnn>。 - Peer Stritzinger

12

使用SUN JavaVM,您可以执行以下操作:

FileDescriptor fd = new FileDescriptor();
sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess().set(fd,3);
FileInputStream fin = new FileInputStream(fd);

5
这是不支持的功能,不是API,并且会在Java 9中发生错误。 - Philippe Marschall
1
那么,对于较新版本的Oracle HotSpot是否有类似的方法?在OpenJDK中呢? - Corrodias

11

最近我需要为一个在jail中运行的Java子进程做这件事。这意味着它无法访问/dev/fd文件系统。

@Bozho发表了一条评论,表示反射可能无法用于创建FileDescriptor对象。不过,在我进行的简单测试中似乎是可以的。以下是TestFD.java的源代码:

import java.lang.reflect.Constructor;
import java.io.FileDescriptor;
import java.io.FileOutputStream;

public class TestFD {
  public static void main(String[] args) throws Exception {
    Constructor<FileDescriptor> ctor = FileDescriptor.class.getDeclaredConstructor(Integer.TYPE);
    ctor.setAccessible(true);
    FileDescriptor fd = ctor.newInstance(3);
    ctor.setAccessible(false);

    new FileOutputStream(fd).write("hi there\n".getBytes());
  }
}
为了测试这个,我写了一个简单的Bash脚本来编译它、设置fd3并运行Java程序:
#!/bin/bash

javac TestFD.java

exec 3>&1  # open fd3, redirect to stdout
java TestFD
exec 3>&-

果然,fd3被重定向到标准输出,并在终端上输出“hi there\n”。注释掉“exec 3>&1”行,Java程序将按预期失败并出现“Device not configured”的IOException。

对私有FileDescriptor构造函数的反射似乎在无法访问/dev/fd的情况下运行良好,并且比尝试使用JNI创建FileDescriptor要简单得多,这是我在其他地方看到的建议。

注意:我在BSD系统上进行了测试。它可能在其他系统上工作,也可能不工作。


2
这在Windows上不起作用,因为唯一的构造函数是默认的。 - FThompson
1
在Linux上运行得非常顺畅。 - Scott Tiger

4

首先:

应用程序不应创建自己的文件描述符

您可以尝试使用反射调用构造函数 private FileDescriptor(int fd),方法是获取构造函数并在其上调用 setAccessible(true)。但这是一种hack方法,我不能保证它会起作用(很可能不会)。特别是考虑到我刚开始给出的引用。


2
我的问题是,我得到的这些文件描述符是由其他语言的运行时和其他外部限制所给出的。因此,这个需求不是我选择的。我会尝试你的建议来解决这个问题。 - Peer Stritzinger
正如你所猜测的那样...它不会起作用。我只能通过Class.getConstructor获取公共构造函数。 - Peer Stritzinger
1
@Peer Stritzinger - 使用 FileDescriptor.getDeclaredConstructor(..) - Bozho
1
打开原始文件描述符是完全有效的操作。否则,父进程如何传递文件呢? - ctrl-alt-delor

0

这段内容最初是由问题的提问者作为自我回答添加的,根据当提问者在编辑中回答自己的问题时该怎么办?的规定,现已移至社区维基回答。


测试通过的解决方案:

使用文件描述符特殊文件系统条目的答案引导我找到了下面可行的解决方案:

  1. 找出您的类Unix系统是否有一个特殊的文件系统,其中包含所有文件描述符的命名条目。

    • 我正在使用FreeBSD,其中fdescfs(5)是一个可以做到这一点的文件系统。在Linux下,它可能是procfs。
  2. 确保该文件系统已挂载

    • FreeBSD:在/etc/fstab中添加fdescfs /dev/fd fdescfs rw 0 0

      或者在shell提示符下运行mount -t fdescfs null /dev/fd(可能需要sudo)

  3. 使用new FileInputStream("/dev/fd/3")new FileOutputStream("/dev/fd/4")来获取与文件描述符连接的流(路径适用于FreeBSD,请替换为您的操作系统路径)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接