8

我有通过 JNI 使用 cpp 共享库 libclient.so 的 java 程序 Client.class。libclient.so 构建为共享并使用 cpp 共享库 libhttp.so。

libclient.so 和 libhttp.so 放在文件夹/home/client/lib64
Client.class 放在/home/client/bin

客户端可以加载库

  1. System.load 和环境变量 LD_LIBRARY_PATH
  2. System.loadLibrary 和 -Djava.library.path

第一种方法效果很好。

export LD_LIBRARY_PATH = /home/client/lib64

java -classpath ./bin 客户端

第二种方式失败。

java -classpath ./bin -Djava.library.path=./../lib64 Client

java.lang.UnsatisfiedLinkError: /home/client/lib64/libclient.so: libhttp.so: cannot open shared object file: No such file or directory

当我将 libhttp.so 放入 /usr/lib64 时,第二种方法可以正常工作。

如果我使用 System.loadLibrary,为什么 libclient.so 会在 /usr/lib64 中寻找 libhttp.so?如何在不将 libhttp.so 复制到 /usr/lib64 的情况下修复它?

我的加载代码:

    //Try load from -Djava.library.path        
    boolean found = false;
    String lib = "client";
    try {
       System.loadLibrary(lib);
       found = true;
    } catch (UnsatisfiedLinkError e) {
       e.printStackTrace();
    }
    //Try load from LD_LIBRARY_PATH
    if (!found) {
       lib = "libclient.so";
       String ld_lib_path = System.getenv("LD_LIBRARY_PATH");
       String[] paths = ld_lib_path.split(":");
       for(int i=0; i<paths.length; i++) {
          String p = paths[i];
          File x = new File(p, lib);
          if (x.exists()) {
             System.load(x.getAbsolutePath());
             found = true;
             break;
          }
       }
    }

附加信息。

如果我用 ldd 测试 libclient.so,我会看到:libhttp.so => not found 如果我设置 export LD_LIBRARY_PATH = /home/client/lib64,那么我会看到:libhttp.so => /home/client/lib64/libhttp.so

4

3 回答 3

10

原因是 libclient.so 是从您的 JVM 加载的,它在java.library.path. 但是,当 libclient.so 尝试加载 libhttp.so 时,它对 Java 一无所知,只是使用常规的 Linux 加载共享库的方式(动态链接器ld.so),它会查找LD_LIBRARY_PATH一些常见的目录,如/usr/lib64.

我可能会LD_LIBRARY_PATH从 Java 应用程序的启动脚本中使用 set。如果您不想使用启动脚本,理论上您可以LD_LIBRARY_PATH从进程本身中设置。但是,Java 不允许这样做(只有System.getenv(),没有System.setenv()),因此您需要编写一个从 Java 调用并调用putenv()setting的小型 C 库LD_LIBRARY_PATH

如果您自己构建libclient.so,您可以使用-rpath链接器标志来指定动态链接器应该查找更多所需库的路径。请注意,如果您在此处指定相对路径,它将被解释为相对于正在运行的应用程序的当前工作目录,而不是相对于libclient.so. 为此,您需要将其$ORIGIN用作参数-rpath并注意您的外壳不会扩展它。

所以,如果你想拥有libclient.solibhttp.so在同一个目录下,你需要使用

-rpath '$ORIGIN'

在构建时作为链接器的参数libclient.so。如果不直接调用链接器而是让编译器调用它,则需要在编译器的命令行中添加以下内容:

-Wl,-rpath,'$ORIGIN'

有关这方面的更多信息,请参见ld.so.

于 2013-04-26T10:33:20.670 回答
2

这个问题我没有很好的答案。

但我找到了几个好方法。

  1. 将 libhttp.so 放入库的共享位置,例如 /usr/lib64。
  2. 将 libhttp.so 的路径放入 LD_LIBRARY_PATH。
  3. 使用 libhttp.so 构建 libclient.so。
  4. 在构建 libclient.so 期间使用 -rpath。
于 2013-04-26T07:24:51.220 回答
0

为了正确查找不同操作系统的库(来自java.library.path)必须具有不同的名称:

  • Linux:libhttp.so
  • 视窗:http.dll

比你可以从 Java 调用:

System.loadLibrary( "http" );
于 2016-06-14T16:38:54.237 回答