You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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即可,但要确保函数内部确实没有用到这个参数。

疑问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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 17:06:32