代码之家  ›  专栏  ›  技术社区  ›  St.Antario

为什么在进入无限递归时JVM不会崩溃?

  •  8
  • St.Antario  · 技术社区  · 6 年前

    我正在编写一个要加载到JVM中的共享库,下面的行为使我陷入困境。以下是我的Java类:

    package com.test;
    
    public class UnixUtil {
        static {
            System.loadLibrary("myfancylibrary");
        }
        static native int openReadOnlyFd(String path);
        static native int closeFd(int fd);
    }
    
    public class Main {
    
        public static void main(String[] args){
            int fd = UnixUtil.openReadOnlyFd("/tmp/testc");
            UnixUtil.closeFd(fd);
        }
    }
    

    要加载的库如下所示:

    TestJiN.H

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class com_test_UnixUtil */
    
    #ifndef _Included_com_test_UnixUtil
    #define _Included_com_test_UnixUtil
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     com_test_UnixUtil
     * Method:    openReadOnlyFd
     * Signature: (Ljava/lang/String;)I
     */
    JNIEXPORT jint JNICALL Java_com_test_UnixUtil_openReadOnlyFd
      (JNIEnv *, jclass, jstring);
    
    /*
     * Class:     com_test_UnixUtil
     * Method:    closeFd
     * Signature: (I)I
     */
    JNIEXPORT jint JNICALL Java_com_test_UnixUtil_closeFd
      (JNIEnv *, jclass, jint);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    

    TestJiN.C

    #include "test_jni.h"
    #include "fs.h"
    
    
    JNIEXPORT jint JNICALL Java_com_test_UnixUtil_openReadOnlyFd
      (JNIEnv *e, jclass jc, jstring path){
      const char *const native_path = ((*e) -> GetStringUTFChars)(e, path, NULL);
      int fd = read_only_open(native_path);
      ((*e) -> ReleaseStringUTFChars)(e, path, native_path);
      return fd;
    }
    
    
    JNIEXPORT jint JNICALL Java_com_test_UnixUtil_closeFd
       (JNIEnv *e, jclass jc, jint fd){
       printf("Closing files descriptord %d... \n", fd);
       return close(fd);
    }
    

    h

    #ifndef FS_H
    #define FS_H
    
    int read_only_open(const char *path);
    
    int close(int fd);
    
    #endif //FS_H
    

    F.C

    #include <unistd.h>
    #include <string.h>
    #include <errno.h>
    #include <stdio.h>
    #include <sys/fcntl.h>
    
    #include "fs.h"
    
    int read_only_open(const char *path){
        printf("Entering %s.%s:%d\n", __FILE__, __func__, __LINE__);
        int fd = open(path, O_RDONLY);
        return fd;
    }
    
    int close(int fd){ //Java_com_test_UnixUtil_closeFd does not invoke this function
        printf("Entering %s.%s:%d\n", __FILE__, __func__, __LINE__);
        int close_result = close(fd);
        return close_result;
    }
    

    编译和运行此 Main JVM类 不坠毁 . 它只是不进入功能 fs.h::close(int) . 相反, stdlib close 如gdb所示:

    Thread 2 "java" hit Breakpoint 1, Java_com_test_UnixUtil_closeFd (e=<optimized out>,
        jc=<optimized out>, fd=4) at /home/rjlomov/test_jni/src/main/java/com/test/lib/test_jni.c:17
    17        return close(fd);
    (gdb) step
    18      }
    (gdb) 
    17        return close(fd);
    (gdb) 
    __close (fd=4) at ../sysdeps/unix/sysv/linux/close.c:27
    27      ../sysdeps/unix/sysv/linux/close.c: No such file or directory.
    (gdb) 
    26      in ../sysdeps/unix/sysv/linux/close.c
    

    运行 objdump -dS libmyfancylibrary.so 表明

    JNIEXPORT jint JNICALL Java_com_test_UnixUtil_closeFd                                                                                                                                                              
      (JNIEnv *e, jclass jc, jint fd){                                                                                                                                                                                 
     7d0:   53                      push   %rbx                                                                                                                                                                        
    }   
    
    //...
    
      return close(fd);                                                                                                                                                                                                
     7e9:   e9 62 fe ff ff          jmpq   650 <close@plt>   // <--- PLT section,
                                          // resolved by linker to stdlib::close?                                                                                                                                                         
     7ee:   66 90                   xchg   %ax,%ax        
    

    问题: 为什么是 stdlib::close 调用 Java_com_test_UnixUtil_closeFd 而不是 fs.c::close(int) ?我唯一能想到的是,JVM有自己的动态链接器来完成这项工作…

    1 回复  |  直到 6 年前
        1
  •  5
  •   apangin    6 年前

    因为您正在编译一个共享库,而函数 close() 不是 static 编译器通过过程链接表(PLT)间接调用。拆卸图书馆时,你很可能会看到一条说明。

        call <close@plt>
    

    什么时候? myfancylibrary 已加载,进程已实现 close 从libc,所以动态liker更新plt指向libc的版本 关闭() .