閱讀928 返回首頁    go 阿裏雲 go 技術社區[雲棲]


《Java 本地接口規範》- 調用 API

調用 API


調用 API 允許軟件廠商將 Java 虛擬機加載到任意的本地程序中。廠商可以交付支持 Java 的應用程序,而不必鏈接 Java 虛擬機源代碼。

本章首先概述了調用 API。然後是所有調用 API 函數的引用頁。

若要增強 Java 虛擬機的嵌入性,可以用幾種方式來擴展 JDK 1.1.2 中的調用 API。


概述

以下代碼示例說明了如何使用調用 API 中的函數。在本例中,C++ 代碼創建 Java 虛擬機並且調用名為 Main.test 的靜態方法。為清楚起見,我們略去了錯誤檢查。

        #include <jni.h>       /* 其中定義了所有的事項 */
 
        ...
 
        JavaVM *jvm;       /* 表示 Java 虛擬機*/
        JNIEnv *env;       /* 指向本地方法接口的指針 */
 
        JDK1_1InitArgs vm_args; /* JDK 1.1 虛擬機初始化參數 */
 
        vm_args.version = 0x00010001; /* 1.1.2 中新增的:虛擬機版本 */
        /* 獲得缺省的初始化參數並且設置類
         * 路徑 */
        JNI_GetDefaultJavaVMInitArgs(&vm_args);
        vm_args.classpath = ...;
 
        /* 加載並初始化 Java 虛擬機,返回 env 中的
         * JNI 接口指針 */
        JNI_CreateJavaVM(&jvm, &env, &vm_args);
 
        /* 用 JNI 調用 Main.test 方法 */
        jclass cls = env->FindClass("Main");
        jmethodID mid = env->GetStaticMethodID(cls, "test", "(I)V");
        env->CallStaticVoidMethod(cls, mid, 100);
 
        /* 結束。*/
        jvm->DestroyJavaVM();

本例使用了 API 中的三個函數。調用 API 允許本地應用程序用 JNI 接口指針來訪問虛擬機特性。其設計類似於 Netscape 的 JRI 嵌入式接口。

 

創建虛擬機

JNI_CreateJavaVM() 函數加載並初始化Java 虛擬機,然後將指針返回到 JNI 接口指針。調用 JNI_CreateJavaVM() 的線程被看作主線程

 

連接虛擬機

JNI 接口指針 (JNIEnv) 僅在當前線程中有效。如果另一個線程需要訪問 Java 虛擬機,則該線程首先必須調用AttachCurrentThread() 以將自身連接到虛擬機並且獲得 JNI 接口指針。連接到虛擬機之後,本地線程的工作方式就與在本地方法內運行的普通 Java 線程一樣了。本地線程保持與虛擬機的連接,直到調用 DetachCurrentThread() 時才斷開連接。

 

卸載虛擬機

主線程不能自己斷開與虛擬機的連接。而是必須調用DestroyJavaVM() 來卸載整個虛擬機。

虛擬機等到主線程成為唯一的用戶線程時才真正地卸載。用戶線程包括 Java 線程和附加的本地線程。之所以存在這種限製是因為 Java 線程或附加的本地線程可能正占用著係統資源,例如鎖,窗口等。虛擬機不能自動釋放這些資源。卸載虛擬機時,通過將主線程限製為唯一的運行線程,使釋放任意線程所占用係統資源的負擔落到程序員身上。


初始化結構

不同的 Java 虛擬機實現可能會需要不同的初始化參數。很難提出適合於所有現有和將來的 Java 虛擬機的標準初始化結構。作為一種折衷方式,我們保留了第一個域 (version) 來識別初始化結構的內容。嵌入到 JDK 1.1.2 中的本地應用程序必須將版本域設置為 0x00010001。盡管其它實現可能會忽略某些由 JDK 所支持的初始化參數,我們仍然鼓勵虛擬機實現使用與 JDK 一樣的初始化結構。

0x800000000xFFFFFFFF 之間的版本號需保留,並且不為任何虛擬機實現所識別。

以下代碼顯示了初始化 JDK 1.1.2 中的 Java 虛擬機所用的結構。

    typedef struct JavaVMInitArgs {
       /* 前兩個域在 JDK 1.1 中保留,並
          在 JDK 1.1.2 中正式引入。*/
       /* Java 虛擬機版本 */
        jint version;
 
       /* 係統屬性。*/
        char **properties;
 
       /* 是否檢查 Java 源文件與已編譯的類文件
        *之間的新舊關係。*/
        jint checkSource;
 
       /* Java 創建的線程的最大本地堆棧大小。*/
        jint nativeStackSize;
 
       /* 最大 Java 堆棧大小。*/
        jint javaStackSize;
 
       /* 初始堆大小。*/
        jint minHeapSize;
 
       /* 最大堆大小。*/
        jint maxHeapSize;
 
       /* 控製是否校驗 Java 字節碼:
        * 0 無,1 遠程加載的代碼,2 所有代碼。*/
        jint verifyMode;
 
       /* 類加載的本地目錄路徑。*/
        const char *classpath;
 
       /* 重定向所有虛擬機消息的函數的鉤子。*/
        jint (*vfprintf)(FILE *fp, const char *format,
                         va_list args);
 
       /* 虛擬機退出鉤子。*/
        void (*exit)(jint code);
 
       /* 虛擬機放棄鉤子。*/
        void (*abort)();
 
       /* 是否啟用類 GC。*/
        jint enableClassGC;
 
       /* GC 消息是否出現。*/
        jint enableVerboseGC;
 
       /* 是否允許異步 GC。*/
        jint disableAsyncGC;
 
       /* 三個保留的域。*/
        jint reserved0;
        jint reserved1;
        jint reserved2;
    } JDK1_1InitArgs;

在 JDK 1.1.2 中,初始化結構提供了鉤子,這樣在虛擬機終止時,本地應用程序可以重定向虛擬機消息並獲得控製權。

當本地線程與JDK 1.1.2 中的 Java 虛擬機連接時,以下結構將作為參數進行傳遞。實際上,本地線程與 JDK 1.1.2 連接時不需要任何參數。JDK1_1AttachArgs 結構僅由 C 編譯器的填充槽組成,而 C 編譯器不允許空結構。

    typedef struct JDK1_1AttachArgs {
       /*
        * JDK 1.1 不需要任何參數來附加
        * 本地線程。此處填充的作用是為了滿足不允許空結構的 C 
        * 編譯器的要求。
        */
        void *__padding;
    } JDK1_1AttachArgs;

調用 API 函數

JavaVM 類型是指向調用 API 函數表的指針。以下代碼示例顯示了這種函數表。

    typedef const struct JNIInvokeInterface *JavaVM;
 
    const struct JNIInvokeInterface ... = {
        NULL,
        NULL,
        NULL,
 
        DestroyJavaVM,
        AttachCurrentThread,
        DetachCurrentThread,
    };

注意,JNI_GetDefaultJavaVMInitArgs()、JNI_GetCreatedJavaVMs() 和JNI_CreateJavaVM()這三個調用 API 函數不是 JavaVM 函數表的一部分。不必先有 JavaVM 結構,就可以使用這些函數。

 

JNI_GetDefaultJavaVMInitArgs

jintJNI_GetDefaultJavaVMInitArgs(void *vm_args);

返回 Java 虛擬機的缺省配置。在調用該函數之前,平台相關代碼必須將 vm_args->version 域設置為它所期望虛擬機支持的 JNI 版本。在 JDK 1.1.2 中,必須將 vm_args->version 設置為 0x00010001。(JDK1.1 不要求平台相關代碼設置版本域。為了向後兼容性,如果沒有設置版本域,則 JDK 1.1.2 假定所請求的版本為 0x00010001。JDK 的未來版本將要求把版本域設置為適當的值。) 該函數返回後,將把vm_args->version 設置為虛擬機支持的實際 JNI 版本。

參數:

vm_args:指向 VM-specific initialization(特定於虛擬機的初始化)結構的指針,缺省參數填入該結構。

返回值:

如果所請求的版本得到支持,則返回“0”;如果所請求的版本未得到支持,則返回負數。

 

JNI_GetCreatedJavaVMs

jintJNI_GetCreatedJavaVMs(JavaVM **vmBuf, jsize bufLen,
jsize *nVMs);

返回所有已創建的Java 虛擬機。將指向虛擬機的指針依據其創建順序寫入 vmBuf 緩衝區。最多寫入 bufLen 項。在 *nVMs 中返回所創建虛擬機的總數。

JDK 1.1 不支持在單個進程中創建多個虛擬機。

參數:

vmBuf:指向將放置虛擬機結構的緩衝區的指針。

bufLen:緩衝區的長度。

nVMs:指向整數的指針。

返回值:

成功時返回“0”;失敗則返回負數。

 

JNI_CreateJavaVM

jintJNI_CreateJavaVM(JavaVM **p_vm, JNIEnv **p_env,
void *vm_args);

加載並初始化Java 虛擬機。當前線程成為主線程。將env 參數設置為主線程的 JNI 接口指針。

JDK 1.1.2 不支持在單個進程中創建多個虛擬機。必須將 vm_args 中的版本域設置為 0x00010001

參數:

p_vm:指向位置(其中放置所得到的虛擬機結構)的指針。

p_env:指向位置(其中放置主線程的 JNI 接口指針)的指針。

vm_args: Java 虛擬機初始化參數。

返回值:

成功時返回“0”;失敗則返回負數。

 

DestroyJavaVM

jintDestroyJavaVM(JavaVM *vm);

卸載 Java 虛擬機並回收資源。隻有主線程能夠卸載虛擬機。調用 DestroyJavaVM() 時,主線程必須是唯一的剩餘用戶線程。

參數:

vm:將銷毀的 Java 虛擬機。

返回值:

成功時返回“0”;失敗則返回負數。

JDK 1.1.2 不支持卸載虛擬機。

 

AttachCurrentThread

jintAttachCurrentThread(JavaVM *vm, JNIEnv **p_env,
void *thr_args);

將當前線程連接到 Java 虛擬機。在 JNIEnv 參數中返回 JNI 接口指針。

試圖連接已經連接的線程將不執行任何操作。

本地線程不能同時連接到兩個 Java 虛擬機上。

參數:

vm:當前線程所要連接到的虛擬機。

p_env:指向位置(其中放置當前線程的 JNI 接口指針)的指針。

thr_args:特定於虛擬機的線程連接參數。

返回值:

成功時返回“0”;失敗則返回負數。

 

DetachCurrentThread

jintDetachCurrentThread(JavaVM *vm);

斷開當前線程與 Java 虛擬機之間的連接。釋放該線程占用的所有 Java 監視程序。通知所有等待該線程終止的 Java 線程。

主線程(即創建 Java 虛擬機的線程)不能斷開與虛擬機之間的連接。作為替代,主線程必須調用 JNI_DestroyJavaVM() 來卸載整個虛擬機。

參數:

vm:當前線程將斷開連接的虛擬機。

返回值:

成功時返回“0”;失敗則返回負數。


 

最後更新:2017-04-02 06:52:01

  上一篇:go assert &lt;assert.h&gt; &lt;cassert&gt;
  下一篇:go Shellcode攻擊實驗