当前位置: 代码迷 >> 综合 >> Java IO (15) -- FileDescriptor 类
  详细解决方案

Java IO (15) -- FileDescriptor 类

热度:14   发布时间:2023-12-16 13:25:02.0

文章目录

    • 1. POSIX标准
    • 2. 文件描述符
    • 3. FileDescriptor 介绍
    • 4. 源码

1. POSIX标准

POSIX 标准是操作系统为应用程序提供的接口,是电子和电气工程协会(简称IEEE)为要在各种 UNIX 操作系统上运行软件而定义的一系列 API 标准总称,目的是为了保证应用程序的可移植性,也就是说为一个 POSIX 操作系统编写的应用程序,在另外一个 POSIX 操作系统里面依旧可以运行,比如:POSIX 标准接口 (Open()打开文件,create()创建文件,read()读取文件,write()写入文件等接口),如果在以 POSIX 标准的操作系统编写的应用程序调用到了 Open() 接口(操作系统提供接口),那么应用程序移植到别的以 POSIX 标准的操作系统,同样也可以运行,因为调用操作系统接口都是一样的,都是 Open()

2. 文件描述符

对内核而言,所有打开的文件(或者socket)都是由文件描述符引用的,打开一个现存的文件或者是创建一个新文件时,内核会向当前进程返回一个文件描述符,那么根据文件描述符就可以找到对应的文件。比如读写一个文件的简单步骤:

  • 应用程序调用 POSIX 标准操作系统接口 open() 或者 create(),方法将会返回文件描述符来标识该文件,将文件描述符的值传递给应用程序中的read()或者write().文件描述符的值结合流就可以读取数据或者写入数据到文件中。

文件描述符是一个非负的整数,在 POSIX 标准的应用程序中,整数 0,1,2 分别表示的是标准输入、标准输出、标准错误输出的描述符。最早期的 UNIX 版本中最大值是 19(也就是容许每个进程打开20个文件),现在系统都有所增加,比如 Linux 是 1024,在 java 中虽然设计使用的是抽象度更高的流,但是依然需要文件描述符与操作系统进行交互

3. FileDescriptor 介绍

在 java 中 FileDescriptor 表示的是"文件描述符",对于文件描述符中 0,1,2 分别表示如下:

  1. 0:标准输入描述符
  2. 1:标准输出描述符
  3. 2:标准错误输出描述符

对于三个"文件描述符",用法基本上是相似的。比如标准输出描述符,看 FileDescriptor 源码,调用的方式是 FileDescriptor.out。但是由于 FileDescriptor 没有直接输出屏幕的接口,需要借助输出流对象,通过调用输出流的方法 write() 方法,可以将对应的字符输出到控制台上,案例如下:

public class FileDescriptorDemo {
    public static void main(String[] args) throws Exception {
    FileOutputStream fos = new FileOutputStream(FileDescriptor.out);fos.write("abcd".getBytes());fos.close();}
}//结果
abcd

当然,在 java 中已经有相应封装的接口,可以直接输出到控制台:System.out.println(“abcd”)。其余标准输入描述符对应是System.in,而标准错误输出对应的是 System.err。

注意:文件描述符就是打开文件或 socket 时,操作系统返回的一个 int 类型值,会根据文件描述符结合流操作对应的文件或者 socket

4. 源码

public final class FileDescriptor {
    //打开的文件或者socket时,简单说操作系统会返回一个fd值,通过该值可以对文件,socket等进行相关操作,private int fd;//关联的流只有一个,则保存到parent中private Closeable parent;//关联的流很多,则保存到集合otherParents中private List<Closeable> otherParents;//用于判断是否释放文件文件引用private boolean closed;//无参数构造方法,fd赋值为-1, 由于java中文件描述符是大于0的整数,所以无意义.public  FileDescriptor() {
    fd = -1;}//有参构造方法,由于修饰符是private,无法设置fdprivate  FileDescriptor(int fd) {
    this.fd = fd;}//标准的输入,文件描述符用0表示public static final FileDescriptor in = new FileDescriptor(0);//标准的输出,文件描述符用1表示public static final FileDescriptor out = new FileDescriptor(1);//标准错误输出,文件描述符用2表示public static final FileDescriptor err = new FileDescriptor(2);//判断是否有效,非-1有效.所以无参构造方法中fd=-1无效public boolean valid() {
    return fd != -1;}public native void sync() throws SyncFailedException;/* This routine initializes JNI field offsets for the class */private static native void initIDs();static {
    initIDs();}// Set up JavaIOFileDescriptorAccess in SharedSecretsstatic {
    sun.misc.SharedSecrets.setJavaIOFileDescriptorAccess(new sun.misc.JavaIOFileDescriptorAccess() {
    public void set(FileDescriptor obj, int fd) {
    obj.fd = fd;}public int get(FileDescriptor obj) {
    return obj.fd;}public void setHandle(FileDescriptor obj, long handle) {
    throw new UnsupportedOperationException();}public long getHandle(FileDescriptor obj) {
    throw new UnsupportedOperationException();}});}//会将文件描述符相关的流添加到otherParents集合中.//结合下面closeAll()方法,释放文件描述符的引用,同时也会将对应所有的流关闭//因为文件描述符释放的情况下,流的存在也无意义synchronized void attach(Closeable c) {
    if (parent == null) {
    // first caller gets to do thisparent = c;} else if (otherParents == null) {
    otherParents = new ArrayList<>();otherParents.add(parent);otherParents.add(c);} else {
    otherParents.add(c);}}//关闭所有与文件描述符关联的所有的流对象@SuppressWarnings("try")synchronized void closeAll(Closeable releaser) throws IOException {
    if (!closed) {
    closed = true;IOException ioe = null;try (Closeable c = releaser) {
    if (otherParents != null) {
    for (Closeable referent : otherParents) {
    try {
    referent.close();} catch(IOException x) {
    if (ioe == null) {
    ioe = x;} else {
    ioe.addSuppressed(x);}}}}} catch(IOException ex) {
    if (ioe != null)ex.addSuppressed(ioe);ioe = ex;} finally {
    if (ioe != null)throw ioe;}}}
}
  相关解决方案