編譯KVM
KVM可以說是JVM中比擬小而且比擬原始的一個版本。KVM是一般放置在嵌進式設備,比如手機等資源有限的終端內運行。KVM沒有采用HotSpotJVM裡面諸多優化技巧,代碼十分簡略,有利於我們懂得Java程序的履行過程。固然KVM裡面履行Java程序可能與現在PC上風行的JVM不太雷同,但是大致原理是類似的。
下面是我在Windows編譯KVM的過程。
1.下載KVM源代碼
http://www.sun.com/software/communitysource/J2ME/cldc/download.XML
現在有cldc1.1和cldc1.04的兩個版本。差別不是很大。我選擇的是1.1的版本的KVM源代碼。
2.下載Cygwin
Sun供給的KVM固然有VC6下編譯的工程,但是全部編譯過程還是有部分需要Cygwin的參與。Cygwin供給了Linux下的make,gcc,grep,find等許多程序,這些程序在KVM裡面的許多makefile裡面都應用到了的。既然是Windows下,那麼只有下載cygwin了。
安裝cygwin的時候,記著把gcc編譯器,make,grep,find等常用的工具裝進往。實在你也可以下載Dev-Cpp這個工具,裡面帶有cygwin的gcc,make但是沒有grep,find這些shell程序。所以還是應當下載一個cygwin。cygwin版本無所謂,很老的都可以。不過cygwin很大就是了。
http://www.cygwin.org
3. 安裝JDK
這個過程就不用我多說了,我是直接安裝的JBuilder2005。
4. make編譯
將j2me_cldc下載下來後,解壓開。進進J2ME_cldc\build\win32目錄。裡面有個makefile文件,這就是win32環境下的編譯文件。
全部過程需要在Windows的命令提示符下完成,還需要設置PATH。在命令提示符下輸進
PATH=D:\CYGWIN\BIN;D:\Borland\JBuilder2005\jdk1.4\bin;%PATH%
然後直接敲進make,就可以進行全部編譯過程了。全部編譯過程應當還是很順利的。
有可能呈現"*.Java 找不到"的錯誤,多半是由於Windows的command裡面有個find.exe和cygwin\bin裡面的那個find.exe沖突了,應當把cygwin\bin放在前面。
5. 在VC6下編譯KVM
在J2ME_cldc\kvm\VmWin\build下有個VC6的dsw工程文件,不過必需先經歷上面的make編譯後才干打開這個VC6工程文件進行編譯。由於全部KVM的編譯需要的兩個文件nativeFunctionTableWin.c和ROMJavaWin.c是在編譯履行tools\jcc後天生的,沒有編譯履行tools\jcc是不會有這兩個文件的。
jcc是個將Java核心的class文件的bytecodes轉換成一堆C語言的數組,然後讓KVM編譯的時候包含進往,這些核心class的bytecodes就是放在上面兩個文件裡面。這樣做的利益就是在KVM履行時不用在再往找核心的class文件然後裝載。
6. 測試運行一下自己編譯的KVM
編譯成功的話,會天生VmWin.exe或者kvm.exe文件。你可以測試一下寫個helloworld.Java,不需要preverifIEr,就可以直接運行你的helloworld.class的。
SUN這個J2MEcldc的KVM裡面已經供給了一個putchar的native function,你可以根據自己的愛好,往增加修正Java的nativefunction。全部KVM.exe有200多K,不過代碼可能只有80K多點,其它的就是Java的CLDC核心class。
-------------------------------------------------------
增加KVM中的系統調用API
大家都曉得KVM是不支撐Native函數調用的,假如要增加一些系統調用的API,那麼只能加到KVM內部。同時,不同的J2ME設備,也有不同的系統調用API以及它們的實現。我們從SUN那麼下載到原始的KVM源代碼,如作甚其增加一個系統調用API呢?本文以具體實踐的步驟一步一步來講解增加KVM系統API的方法。
實在為KVM增加一個系統調用API比為Linux增加一個系統調用API還簡略。大致就分成兩步工作就完成。一步是在classes.zip中增加一個你自己新增的class,一步是在KVM的nativeCore.c中實現這個新增的class的native api函數。
下面以org.test.MiniTest這個新增的class為例來實現一個TestInt()系統調用函數。函數的功效很簡略,就是返回一個整數9999。
1. 新增org.test.MiniTest類
從SUN那裡下載到J2ME_cldc 1.1版本的KVM代碼後。在J2ME_cldc\api目錄下,增加org\test\MiniTest.java的包目錄以及Java文件。然後寫上如下的代碼:
package org.test;
public class MiniTest
{
public static native int TestInt();
}
2. 進行第一次編譯
根據上一篇文章中的KVM編譯方法,在命令提示符下,跳到目錄J2ME_cldc\build\win32下,輸進make命令進行第一次整體編譯。不過,這次編譯過程在編譯連接KVM中的*.o文件的時候,會提示一個找到_Java_org_test_MiniTest_TestInt符號的錯誤提示。
這是由於在我們只是在org.test.MiniTest中定義了這個native函數TestInt,但是並沒有在KVM的任何一個c文件中實現其對應的函數。
首先編譯過程是用javac來編譯J2ME_cldc/api裡面的所有的*.java文件,然後將其class文件打包成一個classes.zip,然後JCC這個工具會默認根據classes.zip天生ROMJavaWin.c和nativeFunctionTableWin.c。而在ROMJavaWin.c聲明這個native函數:
extern void Java_org_test_MiniTest_TestInt(void);
3. 實現Java_org_test_MiniTest_TestInt函數
從KVM中的代碼可以看到,KVM默認都是把一些native函數放到了nativeCore.c這個文件裡面。你也可以自己往新增一些C程序文件,不過本例就把這個Java_org_test_MiniTest_TestInt放在了nativeCore.c文件。
下面是代碼:
void Java_org_test_MiniTest_TestInt(void)
{
pushStack(9999);
}
這裡為什麼把返回值應用pushStack這個宏來返回的原因就不好說了,關於JAVA運行的方法實在就是一個堆棧,Java的字節碼實在就是一種棧式語言,這個在編譯原理裡面的中間代碼天生那一章可以找到它的原型和其闡明。再者,還可以看《Inside Java VirtualMachine》這本書。
4. 第二次編譯
第二次編譯就是可以天生真正的kvm.exe文件了。還是跟第一次編譯以及上一篇編譯KVM的方法一樣,敲進make命令即可。
5. 測試MiniTest.TestInt這個API
自己寫了一個Test的類,來測試這個API:
import org.test.*;
class Test {
public static void main(String[] args) {
System.out.println("Test Result = "+MiniTest.TestInt());
}
}
用Javac編譯的時候,需要把前面的J2ME_cldc/classes.zip拷貝過來,履行:
javac -classpath classes.zip Test.Java
kvm -classpath . Test
------------------------------------------------------------------
應用尺度的KNI增加KVM的系統調用
SUN在宣布的KVM源代碼中實在就已經供給KNI(K NativeInte***ce)這套比擬尺度的API擴大幫助庫。KNI和JNI類似,只是KNI不能象JNI那樣外帶一個DLL文件,KVM通常都是要燒進嵌進式設備的ROM裡面的,所以不能裝載KVM之外的本地代碼。應用KNI來擴大KVM的方法與上一篇文章的方法類似,只是KNI供給了一套功效相對完善的接口。
1. KNI中的數據結構
KJava(或者叫J2ME)中的變量數據結構在KNI中有一套自己的對應的定義。
下面是部分的對應。 Java中的數據結構 KNI中C語言的數據結構 boolean jboolean byte jbyte char jchar int jint
可以在KVM中的kni.h文件找到這些數據類型的定義。
typedef unsigned char jboolean;
typedef signed char jbyte;
typedef unsigned short jchar;
typedef short JShort;
typedef long jint;
typedef float jfloat;
typedef double jdouble;
typedef long JSize;
typedef FIELD jfIEldID;
typedef cell** jobject;
typedef jobject jclass;
typedef jobject jthrowable;
typedef jobject JString;
typedef jobject jarray;
typedef jarray jbooleanArray;
typedef jarray jbyteArray;
typedef jarray jcharArray;
typedef jarray JShortArray;
typedef jarray jintArray;
typedef jarray jlongArray;
typedef jarray jfloatArray;
typedef jarray jdoubleArray;
typedef jarray jobjectArray;
Java中的數據結構
KNI中C語言的數據結構
boolean
jboolean
byte
jbyte
char
jchar
int
jint
可以在KVM中的kni.h文件找到這些數據類型的定義。
typedef unsigned char jboolean;
typedef signed char jbyte;
typedef unsigned short jchar;
typedef short JShort;
typedef long jint;
typedef float jfloat;
typedef double jdouble;
typedef long JSize;
typedef FIELD jfIEldID;
typedef cell** jobject;
typedef jobject jclass;
typedef jobject jthrowable;
typedef jobject JString;
typedef jobject jarray;
typedef jarray jbooleanArray;
typedef jarray jbyteArray;
typedef jarray jcharArray;
typedef jarray JShortArray;
typedef jarray jintArray;
typedef jarray jlongArray;
typedef jarray jfloatArray;
typedef jarray jdoubleArray;
typedef jarray jobjectArray;
2. KNI供給的幫助函數
KNI為了方便大家為它的KVM編寫擴大本地API,還專門編寫了一套比擬完善的幫助函數庫。比如:
KNI_FindClass(const char* name, jclass classHandle);
KNI_GetObjectClass(jobject objectHandle, jclass classHandle);
jfieldID KNI_GetFIEldID(jclass classHandle, const char* name, const char* signature);
jint KNI_GetIntField(jobject objectHandle, jfieldID fIEldID);
這些函數都在kni.h與kni.c中聲明與定義的。假如自己需要編寫一些本地API函數,那麼就可以直接#include "kni.h"來應用這些方便的幫助函數了。
關於這些函數的闡明文檔,KNIspec.pdf也放在了J2ME_cldc/docs裡面,可以參考。
3. 利用KNI實現KVM的本地API
利用KNI實現KVM的本地API的方法和前一篇文章講述的基礎一樣。只是未來能夠應用kni.h和kni.c中的KNI幫助函數,需要在你的native API函數的*.c文件中加上#include "kni.h",其它的就是一樣的。
在SUN供給的KNIspec.pdf文檔的最後還列舉了許多samples以及編譯實現的步驟