剖析Linux核心所支援的Java執行環境

剖析Linux核心所支援的Java執行環境

一,          前言

Sun MicroSystem推出Java技術之後,很快的在全球掀起了一股跨平台技術的風潮,透過Java技術的延伸,我們可以在所開發的環境中擁有撰寫一次程式碼便可輕易在不同平台執行的特性。在這樣的基礎上,從事軟體開發的公司更易於保有他們研發的成果與加速產品上市的時程,同時由於Java除了具有跨平台的特性以外,又十分的注重程式執行環境的安全性,以避免惡意的Java程式破壞正常系統運作,所以在日後的IAInformation Appliance﹞與各式手持裝置上﹝Mobile Phone or PDA﹞都具有極佳的競爭優勢。

        Linux是第一個支援由核心直接執行Java二進位檔的作業系統,在現在Java技術逐漸風行的時刻,這樣的特性尤其吸引人注意,也因此在我們這次的文章中,將特別針對這樣的技術以及在Linux上由Sun所提供的Java VM版本進行介紹。

        目前Java 2共有三個主要的Java VM版本,其中包括了J2EEJava 2 Enterprise Edition﹞、J2SEJava 2 Standard Edition﹞與J2MEJava 2 Micro Edition﹞。

J2EE主要應用是針對企業市場,因此考量上主要針對伺服器端﹝Server-Side﹞程式的開發與應用,例如它的執行環境中支援了Java Beans來支援企業的分散式運算,及Servlet/JSP以供企業應用在網站的服務中。

J2SE主要針對個人電腦用戶的運算平台,在這平台上廣泛支援了一般Java所會使用到的執行環境。而J2ME主要針對執行環境資源有限的平台,例如:嵌入式系統的應用,目前J2ME主要分為兩個版本,分別為CDCConnected Device Configuration ﹞與CLDCConnected Limited Device Configuration ﹞。
       
        J2ME CDC的版本中,提供了一個Java VMCVM C Virtual Machine﹞與一組核心函式庫,主要運作在32-bits的處理器與RAMFlashROM總和在2Mbytes以上的裝置,CDC主要是針對像智慧型手機、汽車導航系統或是資訊家電…….等,具有連網與相當的處理器運算能力。其它與J2ME CDC有關的資訊,各位可以到以下網址查閱即可﹝http://www.sun.com/software/communitysource/j2me/cdc/﹞。

J2ME CLDC版本,提供了一個精簡的Java VMKVMK Virtual Machine﹞,主要運作在RAMFlashROM總和在160—512Kbytes的裝置上,具備低耗電與有限的連網能力,例如現在在Palm OS中所使用的大多為KVM。對於J2ME CLDC相關資訊有興趣的讀者,可以到以下網址去查閱﹝http://www.sun.com/software/communitysource/j2me/cldc/﹞。

       
        如下圖﹝一﹞所示,J2SE支援了Java標準規格中所定義的核心類別函式庫與所有的Java基本型別。J2EE除了支援所有的標準核心類別函式庫以外,還支援了供企業使用的擴充類別函式庫。在最內圍的J2ME主要支援標準核心類別函式庫的子集合與部分的Java基本型別。

圖﹝一﹞,Java 2 各版本所支援的核心類別函式庫範圍示意圖


        如下圖﹝二﹞所示,是筆者在 http://developer.java.sun.com/developer/technicalArticles/wireless/midpapi” 該篇文章中所取得的一張示意圖,它標示出了Java 2 技術在各個不同領域的應用產品。透過這張圖的架構,我們可以很清楚的了解到Java 2的技術除了可以應用在嵌入式領域中,更包括了大型的主機架構與更為精簡型的Java Card﹝透過Card VM﹞。


圖﹝二﹞,Java 2各版本所應用的領域

﹝http://developer.java.sun.com/developer/technicalArticles/wireless/midpapi/﹞



        所以說,在Java的技術架構下,透過Java所開發的應用程式將隨著Java應用的領域不斷擴充與延伸,我們所投資在Java技術的開發成果,都可以隨著應用領域的廣泛,而繼續的擴展開來,相信對於研發軟體的企業來說,這是相當具有投資價值的領域。







        在本文中,我主要把重點放在J2ME的介紹與應用,因為大多數台灣投入Linux領域的廠商,為數不少都是看上了Linux在嵌入式系統的應用,所以說,針對這樣的主題切入,相信可以符合大多數讀者的需求。



        如果讀者需要其它Java不同應用的資訊,可以自行到Sun的Java網站﹝http://java.sun.com﹞去取得相關資訊即可。





二,          建立環境



筆者使用的環境為Red Hat 6.2,使用Java 2 的JDK版本為 1.2.2_008 for Linux﹝可以至http://java.sun.com/products/jdk/1.2/download-linux.html下載﹞。把檔案jdk-1_2_2_008-linux-i386.tar.gz下載後,在/usr/local目錄下解開,會產生一個jdk1.2.2的目錄﹝位於/usr/local/jdk1.2.2﹞。接下來,我們要修改環境執行檔搜尋路徑的參數,例如筆者使用bash shell,就修改檔案 “~/.bash_profile” ,在檔案中加入路徑 “/usr/local/jdk1.2.2/bin”,如下所示



PATH=$PATH:$HOME/bin:/usr/local/jdk1.2.2/bin: 



即可順利使用Java 2的編譯環境。



        各位應該都知道Linux核心可以支援直接執行Java 二進位檔,其實Linux之所以可以支援直接執行Java程式,主要是透過Linux中的MISC檔案系統,這個檔案系統允許使用者加入新的執行檔條件與載入器。如果希望核心支援MISC檔案系統的話,我們必須要在編譯核心時把支援MISC檔案系統的選項加入,如下:



Kernel support for a.out binaries (CONFIG_BINFMT_AOUT) [y/m/N/?]

Kernel support for ELF binaries (CONFIG_BINFMT_ELF) [Y/m/n/?]

Kernel support for MISC binaries (CONFIG_BINFMT_MISC) [Y/m/n/?]




MISC檔案系統載入程式的原理,如下圖﹝三﹞所示
圖﹝三﹞,MISC檔案系統的運作原理



        讀者們可以透過以下Script來進行對MISC檔案系統的註冊,使得在讀取特定的執行檔時可以透過我們所希望的程式載入器來執行

echo ‘:Java:M::\xca\xfe\xba\xbe::/usr/local/jdk1.2.2/bin/javawrapper:’ > /proc/sys/fs/binfmt_misc/register
echo ‘:Applet:E::html::/usr/local/jdk1.2.2/bin/appletviewer:’ > /proc/sys/fs/binfmt_misc/register
echo ‘:Applet:M::<!–applet::/usr/local/jdk1.2.2/bin/appletviewer:’ > /proc/sys/fs/binfmt_misc/register

而每個參數所代表的意義如下表所示

欄位順序
名稱
意義
1
Name
產生在 “/proc/sys/fs/binfmt_misc” 目錄下的檔案名稱,用來識別所支援的執行檔環境
2
Type
表示透過比對檔頭來辨認執行檔
表示透過延伸的副檔名來辨認執行檔
3
Offset
用來標示比對檔頭資料所偏移的位置,通常為0,表示由檔頭的起始位置開始比對
4
Magic
所要比對的檔頭資料或是副檔名的名稱
5
Mask
可以用來mask部分比對字串的位元,通常不使用
6
Interpreter
指向我們所要採用的載入器,而我們使用的執行檔會成為這個載入器的第一個變數


        其中需要注意的是程式 “javawrapper” 這是在JDKJava Development Kit﹞中所沒有包含的,讀者們需要自己來包裝。不過在Linux原始碼的 ” Documentation”目錄下,我們可以找到檔案java.txt,裡面就包含是可以組成程式 javawrapper的程式碼,如下所示

javaclassname.c


#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <sys/types.h>

/* From Sun’s Java VM Specification, as tag entries in the constant pool. */

#define CP_UTF8 1
#define CP_INTEGER 3
#define CP_FLOAT 4
#define CP_LONG 5
#define CP_DOUBLE 6
#define CP_CLASS 7
#define CP_STRING 8
#define CP_FIELDREF 9
#define CP_METHODREF 10
#define CP_INTERFACEMETHODREF 11
#define CP_NAMEANDTYPE 12

/* Define some commonly used error messages */

#define seek_error() error(“%s: Cannot seek\n”, program)
#define corrupt_error() error(“%s: Class file corrupt\n”, program)
#define eof_error() error(“%s: Unexpected end of file\n”, program)
#define utf8_error() error(“%s: Only ASCII 1-255 supported\n”, program);

char *program;

long *pool;

u_int8_t read_8(FILE *classfile);
u_int16_t read_16(FILE *classfile);
void skip_constant(FILE *classfile, u_int16_t *cur);
void error(const char *format, …);
int main(int argc, char **argv);

/* Reads in an unsigned 8-bit integer. */
u_int8_t read_8(FILE *classfile)
{
           int b = fgetc(classfile);
           if(b == EOF)
                     eof_error();
           return (u_int8_t)b;
}

/* Reads in an unsigned 16-bit integer. */
u_int16_t read_16(FILE *classfile)
{
           int b1, b2;
           b1 = fgetc(classfile);
           if(b1 == EOF)
                     eof_error();
           b2 = fgetc(classfile);
           if(b2 == EOF)
                     eof_error();
           return (u_int16_t)((b1 << 8) | b2);
}

/* Reads in a value from the constant pool. */
void skip_constant(FILE *classfile, u_int16_t *cur)
{
           u_int16_t len;
           int seekerr = 1;
           pool[*cur] = ftell(classfile);
           switch(read_8(classfile))
           {
           case CP_UTF8:
                     len = read_16(classfile);
                     seekerr = fseek(classfile, len, SEEK_CUR);
                     break;
           case CP_CLASS:
           case CP_STRING:
                     seekerr = fseek(classfile, 2, SEEK_CUR);
                     break;
           case CP_INTEGER:
           case CP_FLOAT:
           case CP_FIELDREF:
           case CP_METHODREF:
           case CP_INTERFACEMETHODREF:
           case CP_NAMEANDTYPE:
                     seekerr = fseek(classfile, 4, SEEK_CUR);
                     break;
           case CP_LONG:
           case CP_DOUBLE:
                     seekerr = fseek(classfile, 8, SEEK_CUR);
                     ++(*cur);
                     break;
           default:
                     corrupt_error();
           }
           if(seekerr)
                     seek_error();
}

void error(const char *format, …)
{
           va_list ap;
           va_start(ap, format);
           vfprintf(stderr, format, ap);
           va_end(ap);
           exit(1);
}

int main(int argc, char **argv)
{
           FILE *classfile;
           u_int16_t cp_count, i, this_class, classinfo_ptr;
           u_int8_t length;

           program = argv[0];

           if(!argv[1])
                     error(“%s: Missing input file\n”, program);
           classfile = fopen(argv[1], “rb”);
           if(!classfile)
                     error(“%s: Error opening %s\n”, program, argv[1]);

           if(fseek(classfile, 8, SEEK_SET))  /* skip magic and version numbers */
                     seek_error();
           cp_count = read_16(classfile);
           pool = calloc(cp_count, sizeof(long));
           if(!pool)
                     error(“%s: Out of memory for constant pool\n”, program);

           for(i = 1; i < cp_count; ++i)
                     skip_constant(classfile, &i);
           if(fseek(classfile, 2, SEEK_CUR))       /* skip access flags */
                     seek_error();

           this_class = read_16(classfile);
           if(this_class < 1 || this_class >= cp_count)
                     corrupt_error();
           if(!pool[this_class] || pool[this_class] == -1)
                     corrupt_error();
           if(fseek(classfile, pool[this_class] + 1, SEEK_SET))
                     seek_error();

           classinfo_ptr = read_16(classfile);
           if(classinfo_ptr < 1 || classinfo_ptr >= cp_count)
                     corrupt_error();
           if(!pool[classinfo_ptr] || pool[classinfo_ptr] == -1)
                     corrupt_error();
           if(fseek(classfile, pool[classinfo_ptr] + 1, SEEK_SET))
                     seek_error();

           length = read_16(classfile);
           for(i = 0; i < length; ++i)
           {
                     u_int8_t x = read_8(classfile);
                     if((x & 0x80) || !x)
                     {
                                if((x & 0xE0) == 0xC0)
                                {
                                           u_int8_t y = read_8(classfile);
                                           if((y & 0xC0) == 0x80)
                                           {
                                                     int c = ((x & 0x1f) << 6) + (y & 0x3f);
                                                      if(c) putchar(c);
                                                     else utf8_error();
                                           }
                                           else utf8_error();
                                }
                                else utf8_error();
                     }
                     else if(x == ‘/’) putchar(‘.’);
                     else putchar(x);
           }
           putchar(‘\n’);
           free(pool);
           fclose(classfile);
           return 0;
}


javawrapperScript

#!/bin/bash
# /usr/local/bin/javawrapper – the wrapper for binfmt_misc/java

if [ -z “$1″ ]; then
           exec 1>&2
           echo Usage: $0 class-file
           exit 1
fi

CLASS=$1
FQCLASS=`/usr/local/jdk1.2.2/bin/javaclassname $1` è修改javaclassname執行檔路徑
FQCLASSN=`echo $FQCLASS | sed -e ‘s/^.*\.\([^.]*\)$/\1/’`
FQCLASSP=`echo $FQCLASS | sed -e ‘s-\.-/-g’ -e ‘s-^[^/]*$–‘ -e ‘s-/[^/]*$–‘`

# for example:
# CLASS=Test.class
# FQCLASS=foo.bar.Test
# FQCLASSN=Test
# FQCLASSP=foo/bar

unset CLASSBASE

declare -i LINKLEVEL=0

while :; do
           if [ “`basename $CLASS .class`” == “$FQCLASSN” ]; then
                     # See if this directory works straight off
                     cd -L `dirname $CLASS`
                     CLASSDIR=$PWD
                     cd $OLDPWD
                     if echo $CLASSDIR | grep -q “$FQCLASSP$”; then
                                CLASSBASE=`echo $CLASSDIR | sed -e “s.$FQCLASSP$..”`
                                break;
                     fi
                     # Try dereferencing the directory name
                     cd -P `dirname $CLASS`
                     CLASSDIR=$PWD
                     cd $OLDPWD
                     if echo $CLASSDIR | grep -q “$FQCLASSP$”; then
                                CLASSBASE=`echo $CLASSDIR | sed -e “s.$FQCLASSP$..”`
                                break;
                     fi
                     # If no other possible filename exists
                     if [ ! -L $CLASS ]; then
                                exec 1>&2
                                echo $0:
                                echo “  $CLASS should be in a” \
                                     “directory tree called $FQCLASSP”
                                exit 1
                     fi
           fi
           if [ ! -L $CLASS ]; then break; fi
           # Go down one more level of symbolic links
           let LINKLEVEL+=1
           if [ $LINKLEVEL -gt 5 ]; then
                     exec 1>&2
                     echo $0:
                     echo “  Too many symbolic links encountered”
                     exit 1
           fi
           CLASS=`ls –color=no -l $CLASS | sed -e ‘s/^.* \([^ ]*\)$/\1/’`
done

if [ -z “$CLASSBASE” ]; then
           if [ -z “$FQCLASSP” ]; then
                     GOODNAME=$FQCLASSN.class
           else
                     GOODNAME=$FQCLASSP/$FQCLASSN.class
           fi
           exec 1>&2
           echo $0:
           echo “  $FQCLASS should be in a file called $GOODNAME”
           exit 1
fi

if ! echo $CLASSPATH | grep -q “^\(.*:\)*$CLASSBASE\(:.*\)*”; then
           # class is not in CLASSPATH, so prepend dir of class to CLASSPATH
           if [ -z “${CLASSPATH}” ] ; then
                     export CLASSPATH=$CLASSBASE
           else
                     export CLASSPATH=$CLASSBASE:$CLASSPATH
           fi
fi

shift
/usr/local/jdk1.2.2/bin/java $FQCLASS “$@”è修改java編譯程式執行檔路徑



        首先,編譯程式碼 javaclassname.c為執行檔javaclassname,並把執行檔置於目錄 “/usr/local/jdk1.2.2/bin/” 下。接者修改 javawrapperScript檔把執行檔javaclassnameJDK 1.2.2 所附的J2SE Java VM執行檔 “java” 所在的路徑修改正確﹝筆者使用的路徑為 ” /usr/local/jdk1.2.2/bin”﹞。

        現在我們就可以直接在系統的命令列中,直接執行Java的二進位檔了。至於如果要執行Java Applet的話,如同我們之前對MISC檔案系統的註冊,只要是副檔名為 ”html” 或是檔頭為 “<!–applet” 的檔案,就會透過程式“/usr/local/jdk1.2.2/bin/appletviewer” 來執行。而 “appletviewer” 是附屬在JDK的環境中,使用者只要安裝過JDK套件,就會擁有appletviewer 程式了。

由於我們已經在系統中裝好了JDK1.2.2的版本,所以接下來我們也可以用來安裝與編譯J2ME的系統,接下來筆者將以Sun目前所提供的兩個J2ME版本作為介紹的工具。

首先,我們要先取得J2ME CDC for Linux的版本,各位可以到以下網址下載J2ME CDC for Linux 的套件


筆者所下載的版本為 “j2me_cdc-1_0-fcs-src-ar-22_Jan_2001.zip”,不過版本可能隨Sun 更新而有所不同,所以各位只要去下載目前最新的版本即可。在Linux環境中透過指令unzip解壓縮,會產生一個 ”cdcfoundation”的目錄,在目錄 ” cdcfoundation/build/linux” 執行以下命令即可編譯J2ME CDC 除錯用的版本

make CVM_DEBUG=true

        如果不想使用除錯用的版本,就執行以下的參數

        make CVM_DEBUG=false

        這樣在使用cvm時,就不會出現除錯用的訊息了。

        在一切編譯完成後,我以以下這段簡單的Java程式碼為例子

class HelloWorldApp {
        public static void main (String args[]) {
                System.out.println(“Hello World!”);
        }
}

        首先,把它存檔為 “HelloWorldApp.java”,透過程式 javac來進行編譯

[root@Proxy bin]# ls
HelloWorldApp.java  cvm
[root@Proxy bin]# javac HelloWorldApp.java
[root@Proxy bin]# ls
HelloWorldApp.class  HelloWorldApp.java  cvm
[root@Proxy bin]#

        我們還可以透過jarJavaClass檔壓縮,以便於日後在使用Java Class時,便於利用jar所壓縮的Java class壓縮檔,來減少耗用的系統空間,接下來我們執行以下指令

[root@Proxy bin]# jar -cvf HelloWorldApp.jar HelloWorldApp.class
added manifest
adding: HelloWorldApp.class(in = 432) (out= 290)(deflated 32%)
[root@Proxy bin]# ls
HelloWorldApp.class  HelloWorldApp.jar  HelloWorldApp.java  cvm
[root@Proxy bin]#


        現在就可以透過我們剛剛編譯好的J2ME CDC版本的JVM﹝在目錄cdcfoundation/build/linux/bin/﹞來執行Java的程式了,如下

[root@Proxy bin]# ./cvm -Djava.class.path=./HelloWorldApp.jar HelloWorldApp
GC[SS]: Initialized semi-space gen for generational GC
        Size of *each* semispace in bytes=1048576
        Limits of generation = [0x40159200,0x40359200)
        First semispace      = [0x40159200,0x40259200)
        Second semispace     = [0x40259200,0x40359200)
GC[MC]: Initialized mark-compact gen for generational GC
        Size of the space in bytes=3145728
        Limits of generation = [0x40359200,0x40659200)
GC[generational]: Auxiliary data structures
        heapBaseMemoryArea=[0x40159008,0x40659208)
        cardTable=[0x830dc98,0x8310498)
        objectHeaderTable=[0x83104a0,0x8312ca0)
        summaryTable=[0x8312ca8,0x831cca8)
security properties not found. using defaults.
Hello World!
[root@Proxy bin]#


        由於筆者所編譯的cvm為除錯版本,所以會有一些除錯用的訊息,各位如果把 CVM_DEBUG 設為 fasle 就不會有這些訊息出現了。

        當然囉,我們可以把JDK1.2.2中所附J2SE版本的JVM,作為我們透過Linux核心執行Java ClassJVM,當然也可以使用J2ME CDC版本的JVM,所以我們修改檔案“/usr/local/jdk1.2.2/bin/ javawrapper”,把最後一行的

/usr/local/jdk1.2.2/bin/java  $FQCLASS “$@”

        修改為
/home/hlchou/javavm/cdcfoundation/build/linux/bin/cvm -Djava.class.path=./  $FQCLASS  “$@”

        使得我們在執行Java Class時,會直接引用到J2ME CDCJVM,在此我把參數 “java.class.path” 改為 “./” ,這是假設我們會在Java Class所在的目錄中執行Java Class,各位可以根據自己不同的需求來加以修改,修改完後的環境,如下所示

[root@Proxy bin]# ./HelloWorldApp.class
GC[SS]: Initialized semi-space gen for generational GC
        Size of *each* semispace in bytes=1048576
        Limits of generation = [0x40159200,0x40359200)
        First semispace      = [0x40159200,0x40259200)
        Second semispace     = [0x40259200,0x40359200)
GC[MC]: Initialized mark-compact gen for generational GC
        Size of the space in bytes=3145728
        Limits of generation = [0x40359200,0x40659200)
GC[generational]: Auxiliary data structures
        heapBaseMemoryArea=[0x40159008,0x40659208)
        cardTable=[0x830dc98,0x8310498)
        objectHeaderTable=[0x83104a0,0x8312ca0)
        summaryTable=[0x8312ca8,0x831cca8)
security properties not found. using defaults.
Hello World!
[root@Proxy bin]#

我們可以直接在命令列當中執行Java Class,並且所使用的Java VMJ2ME CDC版本。

        J2ME CDC的版本中,如果只計算原本所附的Java標準的核心函式庫,大小約1.65MB。環境中只需要以下兩個檔案

        cdcfoundation/build/linux/bin/cvm
        cdcfoundation/build/linux/lib/cdc.jar
       
        其中,非除錯版本的cvm大小為1.54Mbytes,而cdc.jar大小為119Kbytes。如下所示,為筆者把不必要的檔案移除後,所得的J2ME CDC版本

bash-2.03# du
128     ./lib
1524    ./bin
1684    .
bash-2.03# cd bin
bash-2.03# ls
HelloWorldApp.class  cvm
bash-2.03# cvm -Djava.class.path=./ HelloWorldApp
security properties not found. using defaults.
Hello World!

       
除了介紹J2ME CDC版本以外,我們接下來就介紹J2ME另一個分支J2ME CLDC 的版本。首先,各位可以到以下網址下載J2ME CLDC for Linux 的套件



        筆者所下載的檔案為 “j2me_cldc-1_0_2-fcs-src-b12-winunix-15_Jun_2001.zip”,也許日後SUN會推出更新的版本,所以各位下載時只需要下載最新的J2ME CLDC版本即可。

        同樣的透過  “unzip j2me_cldc-1_0_2-fcs-src-b12-winunix-15_Jun_2001.zip” 來把這個套件解壓縮。

        接下來到目錄 “j2me_cldc/build/linux” 執行 make,即可編譯J2ME CLDC版本的JVM,這個版本的JVM稱為 kvm,之所以稱為 ”k” vm 就是因為這個版本的JVM大小只有 “kilobytes”為單位而已。

        不過在筆者使用的RedHat 6.2中,在我編譯時會在最後Link函式庫時發生錯誤,所以我手動修改檔案 “j2me_cldc/kvm/VmUnix/build/Makefile”,把

“LIBS =    -L/usr/X11R6/lib -lm -lnsl  -lICE -lSM”
改為
“LIBS =    -L/usr/X11R6/lib -lm -lnsl “

        如果有遇到同樣問題的讀者,也可以這樣修正。

        編譯完後,我們可以在目錄 “j2me_cldc/kvm/VmUnix/build” 找到kvm的執行檔,經過strip後大小為200Kbytes左右。如下

[root@Proxy build]# ls -l
total 216
-r–r–r–    1 root     root         3466 Aug 20 05:36 Makefile
-rwxr-xr-x    1 root     root       208888 Aug 20 05:37 kvm
drwxr-sr-x    2 root     root         4096 Aug 20 05:30 obj
[root@Proxy build]#


        當然,我們一樣以之前寫的 “HelloWorldApp.java” 作為測試的範例程式,如下

[root@Proxy build]# ls
HelloWorldApp.java  Makefile  kvm  obj
[root@Proxy build]# javac HelloWorldApp.java
[root@Proxy build]# ./kvm -classpath ./ HelloWorldApp
Hello World!
[root@Proxy build]#


        我們順利的透過kvm執行了HelloWorldApp這個Java Class,同理我們一樣可以把kvm加入到我們的MISC檔案系統所屬的執行環境中,因此我們一樣去修改檔案“/usr/local/jdk1.2.2/bin/ javawrapper”,把最後一行改為

/home/hlchou/javavm/j2me_cldc/kvm/VmUnix/build/kvm

        因此,我們也可以直接在Shell的命令列中直接執行

[root@Proxy kvm]# ./HelloWorld.class
Hello World!
[root@Proxy kvm]#

        就可以透過kvm執行我們的Java Class了。

        要在精簡的環境中使用kvm的話,不包括其它額外的Java函式庫,我們只需要攜帶kvm這個執行檔就可以了,在筆者所包的一個精簡的環境中,只需要一個kvm的執行檔就可以運作了,如下

bash-2.03# pwd
/kvm
bash-2.03# ls -l
total 212
-rwxrwxrwx   1 root     root          354 Aug 16 02:31 HelloWorld.class
-rwxrwxrwx   1 root     root       208888 Aug 16 02:31 kvm
bash-2.03# ./kvm -classpath ./ HelloWorld
Hello World!
bash-2.03#


        其實,目前有許多不同的廠商都提供了J2MEJava VM,筆者在此所舉的例子主要以Sun公司所提供的J2ME版本為主。應用在嵌入式Linux環境時,我們需要考慮的因素還包括J2ME版本的授權費用問題,以及我們還需要包括那些額外的J2ME函式庫,以應用在我們所使用的環境中。

筆者建議各位可以參考如下的網頁


其中文章的第三部份 ” http://www.linuxdevices.com/articles/AT3466014261.html” 介紹了許多 Java VM以及相關的技術,各位如果面臨選擇Java VM版本的問題,或許可以從中得到許多的資訊與參考資料。


三,          MISC檔案系統初始化與運作

如下圖所示,在我們啟動系統後,會呼叫函式init_misc_binfmt進行MISC檔案系統的初始化動作

圖﹝四﹞,MISC檔案系統的初始化與運作

        MISC檔案系統初始化之後,我們可以對檔案 “/proc/sys/fs/binfmt_misc/register” 寫入MISC檔案系統所支援的執行檔格式參數,如下

[root@Proxy binfmt_misc]# pwd
/proc/sys/fs/binfmt_misc
[root@Proxy binfmt_misc]# ls
register  status
[root@Proxy binfmt_misc]# echo ‘:Java:M::\xca\xfe\xba\xbe::/usr/local/jdk1.2.2/b
in/javawrapper:’ > /proc/sys/fs/binfmt_misc/register
[root@Proxy binfmt_misc]# ls
Java  register  status
[root@Proxy binfmt_misc]#

而這個寫入的動作,會傳遞給函式 “proc_write_register”,它會依據 ” :name:type:offset:magic:mask:interpreter:” 的格式,來把載入器與執行檔格式的關係作一個紀錄,以便於之後我們在啟動執行檔時,可以用來進行搜尋與比對的動作。

然後,就可以直接執行Java Class,如下

[root@Proxy /javavm]# java HelloWorld
Hello World!
[root@Proxy /javavm]# ./HelloWorld.class
Hello World!
[root@Proxy /javavm]#


如圖中所示,MISC檔案系統會透過函式check_file來比對執行檔的檔頭,驗證目前所執行的檔案要透過哪一個載入器來執行,並且把載入器程式的位置傳回給load_misc_binary

        最後,在我們關閉系統或是結束MISC檔案系統時,就會進入函式 ”exit_misc_binfmt”,它會取消函式 “load_misc_binary” 對系統的註冊,以避免之後執行與MISC檔案系統相關的執行檔時,會呼叫函式 “load_misc_binary”

之後再依序的把 “/proc/sys/fs/binfmt_misc” 目錄下的檔案移除,並且刪除目錄 “/proc/sys/fs/binfmt_misc”







四,          結語

Java技術在Linux環境上的應用,可以是相當多元的,不論是在個人電腦或是在嵌入式環境的應用,我們都看到了Java在這些領域的大放異彩。筆者曾看過日本NTT DOCOMO所開發支援Java技術的手機,上面可以用來執行用Java撰寫的電玩遊戲,這對日後無線通訊技術與嵌入式領域的開發,是相當具有吸引力與示範作用的。

我們可以幻想這樣的未來,Java技術逐步的應用在許多的家電或是電腦產品中,而Linux也從中取得了關鍵性的作業系統角色與定位。在這樣的世界中,使用者可以透過Java技術所開發的各式應用程式來跨平台控制生活各類家電用品,而Linux則會成為IA或是家用門禁管理系統的核心作業系統,上面會運行一個Java VM通用平台,讓所有的家電或是目前生活環境的最新狀況,隨時透過無線行動通訊的技術傳遞到使用者的手機當中。

未來的科技生活我們很難清楚的預測,不過 成功與失敗就在一次深呼吸之間” ,希望大家都可以從這篇文章有所收穫,進而投入更多的心力在與Linux相關的產業中。


        如有何問題,歡迎隨時與我聯繫。

我的E-Mail: hlchou@mail2000.com.tw



五,          參考文件

[1] The Embedded Java+Linux Quick Reference Guidehttp://www.linuxdevices.com/articles/AT8918758707.html
[2]深入淺出 K JAVA – Java PDA上的程式設計,王森,知城數位
[3] Embedded Linux and Java Technology http://www.kvmworld.com/articles/techtalk/embedded_linux?content_id=1174
[4] Porting the KVM CLDC 1.0 FCS to Linux http://www.kvmworld.com/articles/techtalk/linux?content_id=1026
[5] The J2METM Platform Which APIs Come from the J2SETM Platform?http://developer.java.sun.com/developer/technicalArticles/wireless/midpapi/
[6] Mobile Information Device Profile (MIDP) 
 http://java.sun.com/products/midp/