返回

建置原生共享函式庫

要建置原生共享函式庫,請將命令列引數 --shared 傳遞給 native-image 工具,如下所示

native-image <class name> --shared

要從 JAR 檔案建置原生共享函式庫,請使用以下語法

native-image -jar <jarfile> --shared

產生的原生共享函式庫會將給定 Java 類別的 main() 方法作為其進入點方法。

如果您的函式庫不包含 main() 方法,請使用 -o 命令列選項來指定函式庫名稱,如下所示

native-image --shared -o <libraryname> <class name>
native-image --shared -jar <jarfile> -o <libraryname>

GraalVM 讓使用 C 呼叫原生共享函式庫變得容易。呼叫嵌入原生共享函式庫中的方法(函式)主要有兩種機制:原生映像 C APIJNI 呼叫 API

本指南描述如何使用原生映像 C API。它包含以下步驟

  1. 建立並編譯至少包含一個進入點方法的 Java 類別函式庫。
  2. 使用 native-image 工具從 Java 類別函式庫建立共享函式庫。
  3. 建立並編譯一個 C 應用程式,該應用程式呼叫共享函式庫中的進入點方法。

提示與技巧

共享函式庫必須至少有一個進入點方法。預設情況下,只有名稱為 main() 的方法,源自 public static void main() 方法,才會被識別為進入點,並且可以從 C 應用程式呼叫。

要匯出任何其他 Java 方法

  • 將該方法宣告為 static。
  • 使用 @CEntryPoint (org.graalvm.nativeimage.c.function.CEntryPoint) 註釋該方法。
  • 使該方法的一個參數類型為 IsolateThreadIsolate,例如,下面方法中的第一個參數 (org.graalvm.nativeimage.IsolateThread)。此參數為呼叫提供目前執行緒的執行環境。
  • 將您的參數和回傳類型限制為非物件類型。這些是 Java 基本類型,包括來自 org.graalvm.nativeimage.c.type 套件的指標。
  • 為該方法提供唯一的名稱。如果為兩個公開的方法指定相同的名稱,則 native-image 建置器將會失敗,並顯示 duplicate symbol 訊息。如果未在註釋中指定名稱,則必須在建置時提供 -o <libraryName> 選項。

以下是進入點方法的範例

@CEntryPoint(name = "function_name")
static int add(IsolateThread thread, int a, int b) {
    return a + b;
}

native-image 工具建置原生共享函式庫時,也會產生 C 標頭檔。標頭檔包含 原生映像 C API 的宣告(可讓您從 C 程式碼建立隔離區並附加執行緒),以及共享函式庫中每個進入點的宣告。這是上述範例的 C 標頭宣告

int add(graal_isolatethread_t* thread, int a, int b);

原生共享函式庫可以有無限數量的進入點,例如用於實作回呼或 API。

執行示範

在以下範例中,您會建立一個小的 Java 類別函式庫(包含一個類別),使用 native-image 從類別函式庫建立共享函式庫,然後建立一個小的 C 應用程式來使用該共享函式庫。C 應用程式會將字串作為引數,將其傳遞給共享函式庫,並列印包含引數的環境變數。

先決條件

請確保您已安裝 GraalVM JDK。入門最簡單的方法是使用 SDKMAN!。如需其他安裝選項,請造訪下載區

  1. 將以下 Java 程式碼儲存到名為 LibEnvMap.java 的檔案中

     import java.util.Map;
     import org.graalvm.nativeimage.IsolateThread;
     import org.graalvm.nativeimage.c.function.CEntryPoint;
     import org.graalvm.nativeimage.c.type.CCharPointer;
     import org.graalvm.nativeimage.c.type.CTypeConversion;
    
     public class LibEnvMap {
         //NOTE: this class has no main() method
    
         @CEntryPoint(name = "filter_env")
         private static int filterEnv(IsolateThread thread, CCharPointer cFilter) {
             String filter = CTypeConversion.toJavaString(cFilter);
             Map<String, String> env = System.getenv();
             int count = 0;
             for (String envName : env.keySet()) {
                 if(!envName.contains(filter)) continue;
                 System.out.format("%s=%s%n",
                                 envName,
                                 env.get(envName));
                 count++;
             }
             return count;
         }
     }
    

    請注意如何使用 @CEntryPoint 註釋將方法 filterEnv() 識別為進入點,並在註釋中將方法命名為引數。

  2. 編譯 Java 程式碼並建置原生共享函式庫,如下所示
     javac LibEnvMap.java
    
     native-image -o libenvmap --shared 
    

    它會產生以下成品

     --------------------------------------------------
     Produced artifacts:
     /demo/graal_isolate.h (header)
     /demo/graal_isolate_dynamic.h (header)
     /demo/libenvmap.dylib (shared_lib)
     /demo/libenvmap.h (header)
     /demo/libenvmap_dynamic.h (header)
     ==================================================
    

    如果您使用 C 或 C++,請直接使用這些標頭檔。對於其他語言(例如 Java),請使用標頭中的函式宣告來設定您的外部呼叫繫結。

  3. 在包含以下程式碼的同一目錄中建立 C 應用程式 main.c
     #include <stdio.h>
     #include <stdlib.h>
    
     #include "libenvmap.h"
    
     int main(int argc, char **argv) {
     if (argc != 2) {
         fprintf(stderr, "Usage: %s <filter>\n", argv[0]);
         exit(1);
     }
    
     graal_isolate_t *isolate = NULL;
     graal_isolatethread_t *thread = NULL;
    
     if (graal_create_isolate(NULL, &isolate, &thread) != 0) {
         fprintf(stderr, "initialization error\n");
         return 1;
     }
    
     printf("Number of entries: %d\n", filter_env(thread, argv[1]));
    
     graal_tear_down_isolate(thread);
     }
    

    陳述式 #include "libenvmap.h" 會載入原生共享函式庫。

  4. 使用系統上可用的 clang 編譯器編譯 main.c
     clang -I ./ -L ./ -l envmap -Wl,-rpath ./ -o main main.c 
    

    它會建立一個可執行檔 main

  5. 透過傳遞字串作為引數來執行 C 應用程式。例如
     ./main USER
    

    它會正確列印出相符環境變數的名稱和值。

使用原生映像 C API 的優點是您可以決定 API 的外觀。限制是參數和回傳類型必須為非物件類型。如果您想要從 C 管理 Java 物件,則應該考慮使用 JNI 呼叫 API

與我們聯繫