Java修炼终极指南:158 引入外部链接器API

Foreign Linker API的主要目标是提供一个健壮且易于使用的API(无需编写C/C++代码),以支持Java代码与C/C++本地共享库中的外部函数之间的互操作性(将来,其他编程语言也将通过此API获得支持)。 调用外部代码的旅程始于java.lang.foreign.SymbolLookup功能接口。该接口代表入口点,它由查找加载的本地共享库中给定符号的地址组成。有三种方法可以做到这一点,如下所示:Linker.defaultLookup() – 如其名称所示,defaultLookup()代表默认查找,它扫描并定位当前操作系统所使用所有常用本地共享库的所有符号。Linker linker = Linker.nativeLinker(); SymbolLookup lookup = linker.defaultLookup();SymbolLookup.loaderLookup() – 代表加载器查找,它扫描并定位当前类加载器加载的所有本地共享库中的所有符号(通过System.loadLibrary()和System.load()基于java.library.path)。System.loadLibrary("fooLib"); // 这里加载fooLib.dll SymbolLookup lookup = SymbolLookup.loaderLookup();SymbolLookup.libraryLookup(String name, Arena arena) – 代表库查找,能够扫描并加载arena范围内的给定名称的本地共享库,并为该本地共享库中的所有符号创建符号查找。或者,我们可以通过SymbolLookup.libraryLookup(Path path, Arena arena)指定路径。try (Arena arena = Arena.openConfined()) { SymbolLookup lookup = SymbolLookup.libraryLookup( libName/libPath, arena.scope()); }如果成功完成这一步,那么我们可以选择与我们要调用的外部函数相对应的符号。通过SymbolLookup.find(String name)方法可以按名称找到外部函数。如果指向的方法存在于定位的符号中,则find()返回一个包装在Optional中的零长度内存段(Optional)。这个段的基地址指向外部函数的入口点。MemorySegment fooFunc = mathLookup.find("fooFunc").get();到目前为止,我们已经定位了本地共享库并找到了它的一个方法(fooFunc)。接下来,我们必须将Java代码链接到这个外部函数。这是通过基于两个概念的Linker API实现的:下调用(downcall) - 从Java代码调用本地代码上调用(upcall) - 从本地代码调用Java代码 这两个概念由Linker接口具体化。下调用映射在两个方法中,具有以下签名:MethodHandle downcallHandle(FunctionDescriptor function, Linker.Option... options)default MethodHandle downcallHandle(MemorySegment symbol, FunctionDescriptor function, Linker.Option... options) 通常,使用通过find()方法获得的默认方法,一个描述外部函数签名的函数描述符,以及一组可选的链接器选项。返回的MethodHandle稍后用于通过invoke(),invokeExact()等调用外部函数。通过invoke()或invokeExact()我们向外部函数传递参数并访问运行外部函数返回的结果(如果有的话)。 上调用由以下方法映射:MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, SegmentScope scope) 通常,target参数引用Java方法,function参数描述Java方法签名,scope参数表示与返回的MemorySegment相关联的范围。这个MemorySegment稍后作为调用(invoke()/invokeExact())下调用方法句柄的Java代码的参数传递。因此,这个MemorySegment充当函数指针。 如果我们将这些知识结合起来,那么我们可以编写一个经典的调用getpid()方法的示例,如下所示(考虑阅读有意义的评论以了解每个步骤的见解):// 获取底层本地平台的Linker
// (运行JVM的操作系统+处理器)
Linker linker = Linker.nativeLinker();
       
// "_getpid"是通用C运行时(UCRT)库的一部分
SymbolLookup libLookup = linker.defaultLookup();
       
// 查找"_getpid"外部函数
MemorySegment segmentGetpid = libLookup.find("_getpid").get();
// 为"_getpid"创建方法句柄
MethodHandle func = linker.downcallHandle(segmentGetpid,
  FunctionDescriptor.of(ValueLayout.JAVA_INT));
// 调用外部函数"_getpid"并获取结果
int result = (int) func.invokeExact();      
System.out.println(result);此代码已在Windows 10上测试。如果您运行不同的操作系统,则考虑了解此外部函数以相应地调整代码。


© 版权声明

相关文章

暂无评论

none
暂无评论...