如何使用JNA创建同一个库的多个实例?

Jen*_*ens 7 java jna libraries

我有一个Java Native Access的问题:我有一个带有一个函数的C库,让我们说foo().这个函数有一个内存 - 一个计数器 - 每次调用都会增加.是否可以在同一个java进程中创建此库的两个实例,以便计数器是独立的?

非常感谢你.

这是一些代码:

public class A
{

    public static class Lib
    {
        NativeLibrary libInstance = NativeLibrary.getInstance("myLibrary");
        Function fn = lib.getFunction("foo");
    }

    private Lib lib = new Lib();

    public foo()
    {
        lib.fn.invoke(new Object[] {});
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我打电话:

A a = new A();
A b = new A();

a.foo(); // >1
a.foo(); // >2
b.foo(); // >3
a.foo(); // >4
b.foo(); // >5
a.foo(); // >6
Run Code Online (Sandbox Code Playgroud)

但我想让a和b独立于图书馆工作:

a.foo(); // >1
a.foo(); // >2
b.foo(); // >1
a.foo(); // >3
b.foo(); // >2
a.foo(); // >4
Run Code Online (Sandbox Code Playgroud)

非常感谢

这是我尝试创建lib实例的方法:

public class DriverLib 
{
    private static int counter = 1;

    NativeLibrary lib;

    Function stepAction;
    Function initialize;
    Function terminate;

    Pointer input;
    Pointer output;

    public DriverLib()
    {
        // create options 
        HashMap<String, Integer> options = new HashMap<>();
        options.put(Library.OPTION_OPEN_FLAGS, new Integer(counter++));

        lib = NativeLibrary.getInstance("mylib_win64", options);

        stepAction = lib.getFunction("step");
        initialize = lib.getFunction("initialize");
        terminate  = lib.getFunction("terminate");

        input  = lib.getGlobalVariableAddress("model_U");
        output = lib.getGlobalVariableAddress("model_Y");
    }
}
Run Code Online (Sandbox Code Playgroud)

tec*_*age 5

实现这一目标的最简单方法是简单地使用不同的名称制作共享库的副本。

加载共享库的默认行为是无论加载多少次,都会有效地获得相同的实例。

根据底层操作系统,您可以提供 open 选项,表明您想要一个完全独立的副本,或者一个具有共享代码但独立数据的副本。请参阅LoadLibrary()(windows) 和dlopen()(所有其他) 的文档。您可以通过传递给 的选项将这些选项Library.OPTION_OPEN_FLAGS传递给操作系统Native.loadLibrary()

JNA 可以支持使用任意数量的附加选项加载共享库,并且在 Java 端,它将维护加载了不同选项的相同库作为两个独立库。但是,它通常会将具有相同选项的两个加载视为相同的逻辑共享库(NativeLibrary 表示共享库的任何给定加载,并根据库名称和选项进行缓存)。因此,您可以通过提供一个实际上被忽略的库选项(例如,虚拟类型映射器)来伪造它并加载完全相同的库两次。

请注意,即使您伪造 JNA,您也必须确保传递给底层系统的标志(通过Library.OPTION_OPEN_FLAGS)确保操作系统执行您想要的操作。否则操作系统本身只会将相同的库实例返回给您,而 JNA 对此无能为力。

编辑

找出您需要传递给操作系统的标志,以确保它在您每次调用dlopen/时为您提供一个唯一的句柄LoadLibrary(或至少是一个提供独立数据段的句柄)。您在 Linux 上寻找的标志可能是RTLD_PRIVATE那是要传入的标志Library.OPTION_OPEN_FLAGS。之后,将一个虚拟选项传递给Native.loadLibrary()的选项映射;JNA 应该忽略它无法识别的任何内容,但一个独特的选项将强制 JNA 分别缓存每个库加载。

++idx;
int flags = ...; // Must be flags legal to pass to dlopen/LoadLibraryEx
Map options = new HashMap() {
    { 
        put(Library.OPTION_OPEN_FLAGS, flags); 
        put("ignored-option", idx);
    }
}
lib[idx] = Native.loadLibrary("my-library", options);
Run Code Online (Sandbox Code Playgroud)

查看您的系统是否支持该RTLD_PRIVATE标志。不清楚是否可以在 OSX 或 Windows 上加载具有独立数据段的同一个库,而无需单独的共享库副本。linux下也有dlmopen,但JNA没有使用。

如果您将共享库与 JNA 捆绑在一起,您可以要求 JNA 为您解包 ( Native.extractFromResourcePath()),如有必要,可以多次解压缩(这会为您提供多个共享库副本以供加载)。