1. 引言
在JLI_Launch函数中,先调用CreateExecutionEnvironment函数。CreateExecutionEnvironment函数其中的任务之一是查找到JVM(libjvm.so)的绝对路径jvmpath。接着将jvmpath作为参数之一传递给LoadJavaVM函数。本文将对LoadJavaVM函数进行源码详解。
2. 函数源码详解
LoadJavaVM函数位于 src/solaris/bin/java_md_solinux.c 。该函数相对简单,主要完成两个任务。
任务一是调用dlopen函数根据路径去加载共享库,然后根据参数属性做相应的处理。
void *libjvm;
libjvm = dlopen(jvmpath, RTLD_NOW + RTLD_GLOBAL);
dlopen时glibc提供的函数,用于加载共享库。参数jvmpath为libjvm.so的绝对路径。
RTLD_NOW 表示立即解析所有符号,RTLD_GLOBAL表示符号全局可见。以上代码一句话总结就是根据jvmpath来加载共享库,并立即解析共享库所有符号且符号全局可见。
dlopen函数加载共享库,最终会调用到mmap函数,即调用linux内核提供的sys_mmap去加载so文件。dlopen在glibc-2.35版本的完整调用链如下:
—dlopen位于lfcn/dlopen.c
dlopen
└── __dlopen
└── dlopen_implementation
└──_dlerror_run
└── dlopen_doit
└── _dl_open
└── dl_open_worker
└── dl_open_worker_begin
└── _dl_map_object
└── _dl_map_object_from_fd
└── _dl_map_segments
└── _dl_map_segment
└── __mmap (syscall: mmap/mmap2)
任务二,调用dlsym函数根据符号(函数名)在libjvm找到对应的虚拟地址,并将虚拟地址转换为对应的函数指针。
ifn->CreateJavaVM = (CreateJavaVM_t)
dlsym(libjvm, "JNI_CreateJavaVM");
ifn->GetDefaultJavaVMInitArgs = (GetDefaultJavaVMInitArgs_t)
dlsym(libjvm, "JNI_GetDefaultJavaVMInitArgs");
ifn->GetCreatedJavaVMs = (GetCreatedJavaVMs_t)
dlsym(libjvm, "JNI_GetCreatedJavaVMs");
dlsym也是glibc提供的函数,主要功能是在用户态根据符号在已经加载解析过的共享库中找到对应的虚拟地址,一般不需要进入内核,除非发生了缺页异常。dlsym在glibc-2.35版本的完整调用链如下:
dlsym函数位于 dlfcn/dlsym.c
dlsym
└── __dlsym
└── dlsym_implementation
└── _dlerror_run
└── dlsym_doit
└── _dl_sym
└── do_sym
├── _dl_lookup_symbol_x (查符号表)
├── DL_SYMBOL_ADDRESS (地址计算)
└── _dl_sym_post
ifn指针指向的结构体InvocationFunctions包含三个函数指针。ifn->CreateJavaVM指向JNI_CreateJavaVM函数,这个在后续会被调用来创建JVM。ifn->GetDefaultJavaVMInitArgs指向JNI_GetDefaultJavaVMInitArgs函数。ifn->GetCreatedJavaVMs指向JNI_GetCreatedJavaVMs函数
—src/share/bin/java.h
typedef jint (JNICALL *CreateJavaVM_t)(JavaVM **pvm, void **env, void *args);
typedef jint (JNICALL *GetDefaultJavaVMInitArgs_t)(void *args);
typedef jint (JNICALL *GetCreatedJavaVMs_t)(JavaVM **vmBuf, jsize bufLen, jsize *nVMs);
typedef struct {
CreateJavaVM_t CreateJavaVM;
GetDefaultJavaVMInitArgs_t GetDefaultJavaVMInitArgs;
GetCreatedJavaVMs_t GetCreatedJavaVMs;
} InvocationFunctions;
至此,LoadJavaVM函数源码详解结束
