Linux下C++调用JNIEXPORT函数的两个技术疑问(CentOS7环境)
在CentOS7中调用JNI导出的.so库函数问题解答
刚接触Linux的Windows用户,希望在CentOS7系统中调用.so库(类似Windows的DLL)里的JNIEXPORT函数,以下是相关代码:
test.so中的代码
JNIEXPORT jboolean JNICALL JNI_foo(JNIEnv* env, jobject obj, jstring pathName) { UNREFERENCED_PARAMETER(obj); // implements... return true; }
Linux下的main.cpp代码
#include <dlfcn.h> #include <jni.h> typedef jboolean(JNICALL *Foo_TestFunc)(JNIEnv* env, jobject obj, jstring pathName); int main() { void* handle = dlopen("test.so", RTLD_NOW + RTLD_GLOBAL); if(handle) { *(void**)(&Foo_TestFunc) = dlsym(handle, "JNI_foo"); JNIEnv jenv; jobject jobj; jstring jPath = jenv.NewStringUTF("/home/neohtux/Desktop/test.dat"); jboolean isOk = Foo_TestFunc(&jenv, jobj, jPath); } }
疑问1:调用Foo_TestFunc时,第一个和第二个参数应该传入什么内容?
- 第一个参数(JNIEnv):必须是初始化完成的JVM返回的有效JNIEnv指针*,不能直接声明JNIEnv变量使用。JNIEnv是JNI的核心环境接口,内部关联着JVM的函数表,只有通过JVM初始化流程才能拿到可用的指针。
- 第二个参数(jobject):这个参数的含义取决于JNI函数的类型:
- 如果JNI_foo是Java类的静态方法,则传入对应类的
jclass对象; - 如果是实例方法,则传入该类的
jobject实例; - 由于你的代码里用
UNREFERENCED_PARAMETER(obj)标记了这个参数未被使用,暂时传NULL即可,但要确保函数内部确实没有用到这个参数。
- 如果JNI_foo是Java类的静态方法,则传入对应类的
疑问2:调用jenv.NewStringUTF("/home/neohtux/Desktop/test.dat")时为何出现段错误?
段错误的核心原因是你直接声明了JNIEnv jenv;但没有初始化它。JNIEnv不是可以直接在栈上实例化的普通结构体,它本质是指向JVM内部函数表的指针,未初始化的jenv是野指针,调用它的成员函数NewStringUTF会直接访问非法内存,导致段错误。
修正后的示例代码
要解决这些问题,必须先初始化JVM,获取有效的JNIEnv指针,以下是修正后的main.cpp:
#include <dlfcn.h> #include <jni.h> #include <iostream> typedef jboolean(JNICALL *Foo_TestFunc)(JNIEnv* env, jobject obj, jstring pathName); int main() { // 1. 初始化JVM JavaVM* jvm = nullptr; JNIEnv* env = nullptr; JavaVMInitArgs vm_args; JavaVMOption options[1]; // 设置JVM参数,可根据需求调整类路径等 options[0].optionString = const_cast<char*>("-Djava.class.path=."); vm_args.version = JNI_VERSION_1_8; vm_args.nOptions = 1; vm_args.options = options; vm_args.ignoreUnrecognized = JNI_TRUE; jint ret = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); if (ret != JNI_OK) { std::cerr << "JVM初始化失败" << std::endl; return 1; } // 2. 加载test.so库 void* handle = dlopen("./test.so", RTLD_NOW | RTLD_GLOBAL); if (!handle) { std::cerr << "加载test.so失败: " << dlerror() << std::endl; jvm->DestroyJavaVM(); return 1; } // 3. 获取JNI_foo函数指针 Foo_TestFunc foo_func = reinterpret_cast<Foo_TestFunc>(dlsym(handle, "JNI_foo")); if (!foo_func) { std::cerr << "获取JNI_foo失败: " << dlerror() << std::endl; dlclose(handle); jvm->DestroyJavaVM(); return 1; } // 4. 创建jstring参数 jstring jPath = env->NewStringUTF("/home/neohtux/Desktop/test.dat"); if (!jPath) { std::cerr << "创建jstring失败" << std::endl; dlclose(handle); jvm->DestroyJavaVM(); return 1; } // 5. 调用JNI函数 jboolean isOk = foo_func(env, NULL, jPath); std::cout << "调用结果: " << (isOk ? "成功" : "失败") << std::endl; // 6. 释放资源 env->DeleteLocalRef(jPath); dlclose(handle); jvm->DestroyJavaVM(); return 0; }
编译时需要链接JVM库,示例命令:
g++ main.cpp -o test_jni -ldl -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -L$JAVA_HOME/jre/lib/amd64/server -ljvm
内容的提问来源于stack exchange,提问作者tux Neoh
相关产品推荐
相关产品推荐

