(给ImportNew加星标,提高Java技能)
public static Linker getSystemLinker() {return switch (CABI.current()) {case Win64 - > Windowsx64Linker.getInstance();case SysV - > SysVx64Linker.getInstance();case LinuxAArch64 - > LinuxAArch64Linker.getInstance();case MacOsAArch64 - > MacOsAArch64Linker.getInstance();};}
在 JDK 术语中,链接器是特定于平台的 C ABI 实现的一个实例。链接器提供一组方法来执行向下调用和向上调用,其中:
downcall 是从高级子系统发起的事件。在我们的例子中是 JVM 到较低级别的子系统,如操作系统内核或者一些 Java 代码调用一些本机代码。稍后将通过外部函数和内存 API 说明这一点。
upcall 例如一些本机代码调用一些 Java 代码。
虽然链接器就像电话一样,想打电话给谁,只需拨入正确的电话号码即可。符号查找方法就像通讯录,只需提供要打电话的人正确的信息即可。
要执行向下调用,需要提供调用的(本机)函数的描述符、通过符号查找分配的本机地址,以及用于创建调用本机函数的方法句柄对应的链接器。
从 Java 实现经典的 C 风格的 Hello World:
int printf(const char * __restrict, ...)Linker linker = Linker.nativeLinker();SymbolLookup linkerLookup = linker.defaultLookup();SymbolLookup systemLookup = SymbolLookup.loaderLookup();SymbolLookup symbolLookup = name ->-> linkerLookup.lookup(name));printfMemorySegment = symbolLookup.lookup("printf");
FunctionDescriptor printfDescriptor = FunctionDescriptor.of(JAVA_INT, ADDRESS);注意:从 Java 运行时的角度来看,C 指针背后的值类型无关紧要,因为 C 指针的内存布局不保存类型,而是平台固定的 32/64 位值。
一个描述符定义了一个返回值类型为 int 的函数,它的参数是一个指针。假设一个描述符几乎对应于它在 stdio.h 中的 C 定义,因为它定义了一个标准函数,而 printf 是一个可变参数函数。
在 Java 中,值布局用于对与基本数据类型的值关联的内存布局建模,例如整数类型(有符号或无符号)和浮点类型。JAVA_INT 和 ADDRESS 都是对应的 C 类型的值布局。
JAVA_INT :
// ValueLayout.OfInt.classOfInt JAVA_INT = new OfInt(ByteOrder.nativeOrder()).withBitAlignment(32);
// ValueLayout.OfAddress.classOfAddress ADDRESS = new OfAddress(ByteOrder.nativeOrder()).withBitAlignment(ValueLayout.ADDRESS_SIZE_BITS);
MethodHandle printfMethodHandle = symbolLookup.lookup("printf").map(addr - > linker.downcallHandle(addr, printfDescriptor)).orElse(null);
try (var memorySession = MemorySession.openConfined()) {SegmentAllocator allocator = SegmentAllocator.newNativeArena(memorySession);var cStringFromAllocator = allocator.allocateUtf8String("Hello World" + "\n");var cStringFromSession = memorySession.allocateUtf8String("Hello World" + "\n");}
MemorySegment cString = memorySession.allocateUtf8String(str + "\n");使用分配的内存段,我们可以调用函数: private static int printf(String str, MemorySession memorySession) throws Throwable {Objects.requireNonNull(printfMethodHandle);var cString = memorySession.allocateUtf8String(str + "\n");return (int) printfMethodHandle.invoke(cString);}public static void main(String[] args) throws Throwable {var str = "Hello World";try (var memorySession = MemorySession.openConfined()) {System.out.println(printf(str, memorySession));}}
package com.java_devrel.samples.panama.part_1;import java.lang.foreign.*;import java.lang.invoke.MethodHandle;import java.util.Objects;import static java.lang.foreign.ValueLayout.ADDRESS;import static java.lang.foreign.ValueLayout.JAVA_INT;public class PrintfSimplified {private static final Linker linker = Linker.nativeLinker();private static final SymbolLookup linkerLookup = linker.defaultLookup();private static final SymbolLookup systemLookup = SymbolLookup.loaderLookup();private static final SymbolLookup symbolLookup = name - > systemLookup.lookup(name).or(() - > linkerLookup.lookup(name));private static final FunctionDescriptor printfDescriptor = FunctionDescriptor.of(JAVA_INT.withBitAlignment(32), ADDRESS.withBitAlignment(64));private static final MethodHandle printfMethodHandle = symbolLookup.lookup("printf").map(addr - > linker.downcallHandle(addr, printfDescriptor)).orElse(null);private static int printf(String str, MemorySession memorySession) throws Throwable {Objects.requireNonNull(printfMethodHandle);var cString = memorySession.allocateUtf8String(str + "\n");return (int) printfMethodHandle.invoke(cString);}public static void main(String[] args) throws Throwable {var str = "hello world";try (var memorySession = MemorySession.openConfined()) {System.out.println(printf(str, memorySession));}}}
转自:Denys Makogon,
链接:denismakogon.github.io/openjdk/panama/2022/05/31/introduction-to-project-panama-part-1.html
- EOF -
看完本文有收获?请转发分享给更多人
关注「ImportNew」,提升Java技能
点赞和在看就是最大的支持❤️