Java虛擬機應用于數字電視機頂盒的研究與實(shí)現
——
引 言
隨著(zhù)有線(xiàn)廣播電視網(wǎng)向數字化、網(wǎng)絡(luò )化、產(chǎn)業(yè)化方向發(fā)展,利用有線(xiàn)電視網(wǎng)絡(luò )作為傳輸平臺的數字電視機頂盒除了能夠讓用戶(hù)在現有模擬電視機上觀(guān)看數字電視節目之外,廣播和交互式多媒體應用功能的要求也應運而生,互動(dòng)電視成為數字電視發(fā)展的方向。眾多程序員熟悉的為網(wǎng)絡(luò )廣泛運用的Java語(yǔ)言能很好地滿(mǎn)足機頂盒一些服務(wù)應用的要求,因此提出了包含Java虛擬機的數字電視機頂盒中間件的系統架構,該虛擬機用來(lái)執行Java應用程序,并且中間件將應用程序和底層操作系統、硬件細節隔離開(kāi),使上層的數字電視的服務(wù)應用不必考慮過(guò)多的底層細節。本文主要介紹了J2ME中主要用于數字電視領(lǐng)域的CDC的移植,其中又著(zhù)重介紹了本地方法(native method)的實(shí)現過(guò)程。
結構及工作原理
Java虛擬機工作原理
Java虛擬機處于機器和編譯程序之間,在任何平臺上都提供給編譯程序一個(gè)共同的接口。Java源程序經(jīng)過(guò)編譯器編譯后變成字節碼,字節碼由虛擬機解釋執行,虛擬機將每一條要執行的字節碼送給解釋器,解釋器將其翻譯成特定機器上的機器碼,然后在特定的機器上運行。
Java虛擬機的主要任務(wù)是裝載class文件并且執行其中的字節碼。Java虛擬機包含一個(gè)類(lèi)裝載器,它可以從程序和API中裝載class文件。字節碼由執行引擎來(lái)執行,具體過(guò)程如圖1所示。

圖1 Java虛擬機的工作過(guò)程
Java虛擬機結構
類(lèi)裝載器的體系結構是Java虛擬機在安全性和網(wǎng)絡(luò )移動(dòng)性上發(fā)揮重要作用的一個(gè)方面,圖中所示的類(lèi)裝載器可以包含多個(gè)類(lèi)裝載器的子系統, Java應用程序能夠在運行時(shí)決定需要安裝的類(lèi),并且將被不同的類(lèi)裝載器裝載的類(lèi)存放在不同的命名空間。
執行引擎處于Java虛擬機的核心位置,它的行為由指令集所決定,其主要作用就是解釋字節碼(即運行經(jīng)過(guò)編譯后的Java程序的class文件) ,不同的執行引擎實(shí)現可能非常不同。由軟件實(shí)現的虛擬機的執行引擎分為一次性解釋字節碼、即時(shí)編譯器和自適應優(yōu)化器,由硬件芯片構成的虛擬機用本地方法執行Java字節碼,它的執行引擎是內嵌在芯片里。
Java虛擬機相當于一個(gè)堆棧計算機,它在指令間傳送信息時(shí)不使用任何物理寄存器,而使用堆棧的幀來(lái)表示方法的狀態(tài)、字節碼的操作對象、方法的參數空間及局部變量的空間,它的“程序計數器”為一個(gè)偽寄存器,是當前所執行指令的字節碼數組的一個(gè)指針。
Java實(shí)現方法
Java有兩種實(shí)現方法:Java方法和本地方法。Java方法是由Java 語(yǔ)言編寫(xiě),編譯成字節碼,存儲在class文件中。本地方法是由其他語(yǔ)言(比如C,C++,或者匯編語(yǔ)言)編寫(xiě)的,編譯成和處理器相關(guān)的機器代碼,保存在動(dòng)態(tài)連接庫中,格式是各個(gè)平臺專(zhuān)有的,它是聯(lián)系Java程序和底層主機操作系統的連接方法。Java方法與平臺無(wú)關(guān),但是本地方法卻不是,運行中的Java程序調用本地方法時(shí),虛擬機裝載包含這個(gè)本地方法的動(dòng)態(tài)庫,并調用這個(gè)方法。通過(guò)本地方法, Java程序可以直接訪(fǎng)問(wèn)底層操作系統的資源,使程序和特定的平臺相關(guān),一個(gè)本地方法接口——Java本地接口(JNI)使得本地方法可以在特定的主機系統的任何一個(gè)Java平臺上運行。
J2ME中的CDC移植的解決方案
我們選用的要移植的Java虛擬機是Sun公司提供的J2ME對于數字電視領(lǐng)域CDC配置,該虛擬機是針對Linux的軟件實(shí)現(用C語(yǔ)言編寫(xiě)的虛擬機,該虛擬機也稱(chēng)為C Virtual Machine,簡(jiǎn)稱(chēng)CVM) 。Java虛擬機規范并沒(méi)有強求Java虛擬機必須支持任何特定的本地方法接口,但是Sun 公司提供了本地方法接口(Java Native Interface) ,是為移植所用。Java程序通過(guò)調用本地方法和主機交互。
Java虛擬機

圖2 Java虛擬機的位置
虛擬機處于嵌入式操作系統OS20 上,因此Sun公司提供的Java虛擬機中與底層操作系統相關(guān)的操作都應該替換為OS20的內核函數。因此將Sun公司提供的CDC移植到OS20需要完成一些工作,比如: C語(yǔ)言中關(guān)于基本數據類(lèi)型的數據位的修改,關(guān)于線(xiàn)程的創(chuàng )建機制(OS20為任務(wù)) ,關(guān)于線(xiàn)程之間的同步、互斥,關(guān)于動(dòng)態(tài)連接的實(shí)現,關(guān)于本地方法的實(shí)現等,本節主要介紹關(guān)于本地方法的實(shí)現過(guò)程。
本地方法
解釋器處理字節碼時(shí),與給定字節碼有關(guān)的動(dòng)作的語(yǔ)義、執行字節碼的相關(guān)動(dòng)作大多是從堆棧中獲得其操作數,并將其結果送回堆棧中。典型的情況下字節碼是帶有參數的,這些參數在字節碼流中緊跟在字節碼自身之后。
在虛擬機解釋字節碼過(guò)程中,執行引擎會(huì )不時(shí)遇到請求本地方法調用的指令,虛擬機負責試著(zhù)發(fā)起這個(gè)本地方法的調用。本地方法是Java虛擬機指令集的一種可編程擴展,運行這個(gè)本地方法就是Java虛擬機對這條指令的執行。
本地方法函數調用
為了增加虛擬機的性能,加快其速度,解釋器在處理一些字節碼時(shí)調用的本地方法函數用匯編實(shí)現了將Java棧轉換為C棧,然后在C堆棧上實(shí)現函數的調用。Linux下是用獨立的匯編語(yǔ)言程序invokeNative_i386。 S實(shí)現函數CVMjniInvokeNative () ,我們采用在C里面嵌入匯編的形式來(lái)實(shí)現該函數。
該函數的形參有7個(gè),完成的主要功能是將由實(shí)參傳遞來(lái)的部分數據通過(guò)直接或者運算后得到本地方法的參數,然后壓入本地棧,通過(guò)匯編來(lái)實(shí)現本地的C函數調用。實(shí)參傳遞過(guò)來(lái)的7個(gè)數據包含JN I環(huán)境指針(env) 、本地方法的函數指針(native code) 、Java棧指針(args) 、本地方法的描述符(terse sig) , Java棧的參數總數(args size)表示靜態(tài)或非靜態(tài)方法的類(lèi)對象標志(class object)及用于存儲返回值的一個(gè)指針變量(return value) ,其中env要作為第一個(gè)本地方法的參數傳遞,并且native code也要傳遞到本地方法來(lái)實(shí)現本地方法的正確調用。
J2ME中的CDC移植
由于Linux有多個(gè)通用寄存器,在實(shí)現該函數的代碼中充分運用了如esp、ebp、esi等寄存器,但是OS20提供的可操作的寄存器只有3個(gè)通用寄存器Areg、Breg、Creg和1個(gè)工作指針寄存器Wptr (相當于堆棧指針) ,在實(shí)現過(guò)程中,我們用在C函數中設立局部變量來(lái)代替Linux的通用寄存器,通過(guò)手動(dòng)調整工作棧指針來(lái)實(shí)現本地方法的調用,具體實(shí)現過(guò)程如圖3所示。

圖3 Java棧到本地棧的轉換
當進(jìn)入匯編函數時(shí),工作區指針為Wptr,實(shí)參、狀態(tài)寄存器和指令指針寄存器的值全部自動(dòng)入棧,然后是我們定義的代替Linux寄存器的局部變量自動(dòng)入棧,此時(shí)的Wptr自動(dòng)移到Wptr′,利用OS20的匯編指令,手動(dòng)將實(shí)參傳遞過(guò)來(lái)的參數通過(guò)計算得到本地方法參數的個(gè)數,然后將本地方法所需的參數依次壓棧,最后再手動(dòng)調節工作區指針實(shí)現本地方法的成功調用。這里我們先將本地方法函數指針和1個(gè)標志位flag(0x10101010)入棧,原因有兩個(gè):
①當隨后我們手動(dòng)調節工作指針Wptr′到Wptr"時(shí),工作棧已由先前的嵌套匯編的函數進(jìn)入到了要調用的本地方法的C函數,因此先前的函數的局部變量在此時(shí)無(wú)效,也就是說(shuō)此時(shí)如果用以前實(shí)參傳遞過(guò)來(lái)的本地函數指針調用本地的函數肯定不會(huì )成功,因此要把這個(gè)函數指針先手動(dòng)保存起來(lái)。
②flag的設置的原因是:本地方法的參數的個(gè)數不是固定的,而OS20 所提供的用匯編調用函數在回到函數入口點(diǎn)時(shí)只彈出Wptr"指向的4 個(gè)單元的內容,因此多余的參數出棧操作也必須通過(guò)調節Wptr手動(dòng)完成,通過(guò)向下移動(dòng)Wptr查找flag標志,再調節Wp tr到Wtpr+2即可正確地回到匯編函數。當本地方法的參數完全手動(dòng)入棧后就可以手動(dòng)調節工作區指針Wptr而進(jìn)入到調用的本地函數,函數返回后的第一件事是保存在寄存器中的函數的返回值到return value,恢復工作區指針Wptr,并將本地方法的返回值類(lèi)型作為嵌入匯編的函數的返回值,此時(shí)就完成了由Java棧到C棧的轉換,并成功調用本地方法。
結 論
通過(guò)對Sun公司下載的CDC代碼的修改與編寫(xiě),對CDC中的加載的類(lèi)做了一些裁減,并且將線(xiàn)程化的解釋器改為一次性解釋字節碼的單線(xiàn)程解釋器簡(jiǎn)化程序,成功地生成了在機頂盒上可以運行簡(jiǎn)單的Java程序的虛擬機。
評論