【精】Java中如何获取文件描述符?

关于什么是文件描述符,见文末的链接⑥

先说下为什么写这篇文章。

在开发中,有这么个需求:想通过InputStream获取到打开文件的文件描述符的int值。

但是目前只能通过流的getFD()方法获取到FileDescriptor对象,然后FileDescriptor对象并没有提供返回文件描述符整数值的方法:

String path = "/xxx/xxx/xxx.txt";
RandomAccessFile raf = new RandomAccessFile(path,"r");
FileDescriptor fd = raf.getFD();

因此就调研了一些获取文件描述符整型值的方案。

有两种方案:
① 通过native方法去做。
② 通过SharedSecrets去做。

一、通过native方法获取FileDescriptor对象代表的整数值

Tips:Hadoop中就是采用了这种方案。

1.1 JNI预备知识

涉及到的JNI函数有:GetFieldID()、GetIntField()。

读者可参考JNI文档对函数的功能、参数意义等进行详细查阅,这里我用简短的话描述一下这两个函数。https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#GetFieldID

① GetFieldID()函数原型:

jfieldID GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);

用于返回一个成员变量(也就是非静态的)的成员ID,这个ID用于Get这个成员变量的值,或者Set这个成员变量的值。

② GetIntField()函数原型:

NativeType GetIntField(JNIEnv *env, jobject obj, jfieldID fieldID);

很好理解,就是获取obj对象里jfieldID的成员变量的值。

1.2 具体实现

我们在java代码中定义一个native方法:

public native int getIntFileDescriptor(FileDescriptor fd);

然后通过javah生成.h头文件,进行具体实现。在具体的实现中可利用如下代码:

// 整数类型fd的fieldID
static jfieldID fd_descriptor;

void fd_init(JNIEnv* env) {
// 通过GetFieldID获得整型成员变量fd的fieldID,赋值给上面声明的fd_descriptor。
fd_descriptor = (*env)->GetFieldID(env, fd_class, "fd", "I");
}

/*
 * Given an instance 'obj' of java.io.FileDescriptor, return the
 * underlying fd, or throw if unavailable
 */
int fd_get(JNIEnv* env, jobject obj) {
  if (obj == NULL) {
    THROW(env, "java/lang/NullPointerException",
          "FileDescriptor object is null");
    return -1;
  }
  // 通过GetIntField获取到整型值,参数中的fd_descriptor在上面已经被赋值过了。
  return (*env)->GetIntField(env, obj, fd_descriptor);
}

二、通过SharedSecrets获取FileDescriptor对象代表的整数值

这种方案是我在阅读FileDescriptor源码时发现的线索。开始时在FileDescriptor类中想寻找get方法。

get方法没找到,不过找到了一些获取文件描述符方法的蛛丝马迹。

于是我用如下代码成功获取到了整型的文件描述符(并通过输出结果 + strace跟踪系统调用验证结果是正确的):

String path = "/home/xxxx/xxxx.java";
    RandomAccessFile randomAccessFile = new RandomAccessFile(path,"r");
    int fdint = SharedSecrets.getJavaIOFileDescriptorAccess().get(randomAccessFile.getFD());
    System.out.println("file des: " + fdint);

三、理解SharedSecrets

SharedSecrets这个类是sun.misc包下的,供内部使用,不建议Java开发者使用。我们找到openJDK的源码(https://github.com/frohoff/jdk8u-jdk/blob/master/src/share/classes/sun/misc/SharedSecrets.java),看其中的相关注释。英文注释总结起来就是一句话:SharedSecrets提供了不使用反射的情况下在其他包中调用私有实现方法的一种机制,。

/* A repository of “shared secrets”, which are a mechanism for
calling implementation-private methods in another package without
using reflection. A package-private class implements a public
interface and provides the ability to call package-private methods
within that package; the object implementing that interface is
provided through a third package to which access is restricted.
This framework avoids the primary disadvantage of using reflection
for this purpose, namely the loss of compile-time checking. 
*/

回顾一下Java的访问权限修饰词:

类成员访问权限修饰词有四类:private(只能在当前类中被访问),无(在同一个包下可以访问),protected(同一个包下和子类可以访问) 和 public(所有类都可以访问)。

所以SharedSecrets要解决的问题就是在包B中能够访问到包A中的private类型的实现方法。这样的实现方式不会将A中的private类型的方法暴露给所有用户。

更多关于SharedSecrets使用方式在文章最后的链接里。

参考
①JDK源码
②Hadoop-3.1.0源码
https://stackoverflow.com/questions/46722452/how-does-the-sharedsecrets-mechanism-work
https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#GetFieldID
https://stackoverflow.com/questions/46722452/how-does-the-sharedsecrets-mechanism-work
http://c.biancheng.net/view/3066.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叹了口丶气

觉得有收获就支持一下吧~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值