Java的readBytes是怎么实现的?

1.前言

众所周知,Java是一门跨平台语言,针对不同的操作系统有不同的实现。本文从一个非常简单的api调用来看看Java具体是怎么做的.

2.源码分析

从FileInputStream.java中看到readBytes最后是native调用

/**      * Reads a subarray as a sequence of bytes.      * @param b the data to be written      * @param off the start offset in the data      * @param len the number of bytes that are written      * @exception IOException If an I/O error has occurred.      */     private native int readBytes(byte b[], int off, int len) throws IOException; // native调用      /**      * Reads up to <code>b.length</code> bytes of data from this input      * stream into an array of bytes. This method blocks until some input      * is available.      *      * @param      b   the buffer into which the data is read.      * @return     the total number of bytes read into the buffer, or      *             <code>-1</code> if there is no more data because the end of      *             the file has been reached.      * @exception  IOException  if an I/O error occurs.      */     public int read(byte b[]) throws IOException {         return readBytes(b, 0, b.length);     }

从jdk源码中,我们找到FileInputStream.c(/jdk/src/share/native/java/io),此文件定义了对应文件的native调用.

// FileInputStream.c  JNIEXPORT jint JNICALL Java_java_io_FileInputStream_readBytes(JNIEnv *env, jobject this,         jbyteArray bytes, jint off, jint len) {     return readBytes(env, this, bytes, off, len, fis_fd); }

我们观察下当前的目录,可以看到java 对典型的四种unix like的系统(bsd, linux, macosx, solaris), 以及windows 提供了特殊实现。share是公用部分。

Java的readBytes是怎么实现的?

在头部获取文件fd field (fd 是非负正整数,用来标识打开文件)

// FileInputStream.c  JNIEXPORT void JNICALL Java_java_io_FileInputStream_initIDs(JNIEnv *env, jclass fdClass) {     fis_fd = (*env)->GetFieldID(env, fdClass, "fd", "Ljava/io/FileDescriptor;"); /* fd field,后面用来获取 fd */ }

 继续调用readBytes

// ioutil.c  jint readBytes(JNIEnv *env, jobject this, jbyteArray bytes,           jint off, jint len, jfieldID fid) {     jint nread;     char stackBuf[BUF_SIZE];     char *buf = NULL;     FD fd;      if (IS_NULL(bytes)) {         JNU_ThrowNullPointerException(env, NULL);         return -1;     }      if (outOfBounds(env, off, len, bytes)) { /* 越界判断 */         JNU_ThrowByName(env, "java/lang/IndexOutOfBoundsException", NULL);         return -1;     }      if (len == 0) {         return 0;     } else if (len > BUF_SIZE) {         buf = malloc(len); /* 缓冲区不足,动态分配内存 */         if (buf == NULL) {             JNU_ThrowOutOfMemoryError(env, NULL);             return 0;         }     } else {         buf = stackBuf;     }      fd = GET_FD(this, fid); /* 获取fd */     if (fd == -1) {         JNU_ThrowIOException(env, "Stream Closed");         nread = -1;     } else {         nread = IO_Read(fd, buf, len); /* 执行read,系统调用 */         if (nread > 0) {             (*env)->SetByteArrayRegion(env, bytes, off, nread, (jbyte *)buf);         } else if (nread == -1) {             JNU_ThrowIOExceptionWithLastError(env, "Read error");         } else { /* EOF */             nread = -1;         }     }      if (buf != stackBuf) {         free(buf); /* 失败释放内存 */     }     return nread; }

我们继续看看IO_Read的实现,是个宏定义

#define IO_Read handleRead

handleRead有两种实现

solaris实现:

// /jdk/src/solaris/native/java/io/io_util_md.c  ssize_t handleRead(FD fd, void *buf, jint len) {     ssize_t result;     RESTARTABLE(read(fd, buf, len), result);     return result; }  /*  * Retry the operation if it is interrupted  */ #define RESTARTABLE(_cmd, _result) do {      do {          _result = _cmd;      } while((_result == -1) && (errno == EINTR));  /* 如果是中断,则不断重试,避免进程调度等待*/ } while(0)

read方法可以参考unix man page

windows实现:

// jdk/src/windows/native/java/io/io_util_md.c  JNIEXPORT jint handleRead(FD fd, void *buf, jint len) {     DWORD read = 0;     BOOL result = 0;     HANDLE h = (HANDLE)fd;     if (h == INVALID_HANDLE_VALUE) {         return -1;     }     result = ReadFile(h,          /* File handle to read */                       buf,        /* address to put data */                       len,        /* number of bytes to read */                       &read,      /* number of bytes read */                       NULL);      /* no overlapped struct */     if (result == 0) {         int error = GetLastError();         if (error == ERROR_BROKEN_PIPE) {             return 0; /* EOF */         }         return -1;     }     return (jint)read; }

3.java异常初探

// jdk/src/share/native/common/jni_util.c  /**  * Throw a Java exception by name. Similar to SignalError.  */ JNIEXPORT void JNICALL JNU_ThrowByName(JNIEnv *env, const char *name, const char *msg) {     jclass cls = (*env)->FindClass(env, name);      if (cls != 0) /* Otherwise an exception has already been thrown */         (*env)->ThrowNew(env, cls, msg); /* 调用JNI 接口*/ }  /* JNU_Throw common exceptions */  JNIEXPORT void JNICALL JNU_ThrowNullPointerException(JNIEnv *env, const char *msg) {     JNU_ThrowByName(env, "java/lang/NullPointerException", msg); }

最后是调用JNI:

// hotspot/src/share/vm/prims/jni.h  jint ThrowNew(jclass clazz, const char *msg) {         return functions->ThrowNew(this, clazz, msg);     }  jint (JNICALL *ThrowNew)       (JNIEnv *env, jclass clazz, const char *msg);

4.总结

很多高级语言,有着不同的编程范式,但是归根到底还是(c语言)系统调用,c语言能够在更低的层面做非常多的优化。如果我们了解了这些底层的系统调用,就能看到问题的本质。

本文没有对JNI 做深入分析,后续继续解析。

5.参考

https://man7.org/linux/man-pages/man2/read.2.html

发表评论

评论已关闭。

相关文章