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 b.length 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
- * -1 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是公用部分。

在头部获取文件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 handleReadhandleRead有两种实现
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
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |