經驗表明,C語言的可移植性、開發速度及內置的運行時間支持(C標準庫)等優勢遠比使用該語言成本較低的特點更重要。Java語言自出現以來也是如此。事實上,采用Java編寫的系統級軟件具有更好的成本-效益比,這使得Java成為目前和將來嵌入式設計的語言之一。
確切地說,什么是系統級編程呢?對大多數開發者來說,是指在系統的層進行編程,直接與硬件對話并控制硬件。設備驅動程序是系統級編程的一個典型例子。它們一般與操作系統(OS)和硬件直接相連,以便讓設備以某種標準方式存取應用級程序。應注意的是,有些類型的設備驅動程序不能用Java編寫。例如,在很多OS中,驅動程序要么作為共享目標被裝載,要么與內核本身靜態鏈接。對于這種OS,不能用Java編寫設備驅動程序,因為Java虛擬機(JVM)是在內核外運行的。
Java標準庫提供了很多函數。然而,它并不總能提供直接與底層系統接口所需的特性。輸入JNI,就可以安全且輕便地從共享庫中調用本地函數。這些函數可以與類交互,剔除異常(exception),就跟很多驅動程序都是由三部分組成:硬件初始化、中斷處理和客戶端接口。在詳細探討這三個組成部分之前,先介紹一些關鍵的Java概念,特別是Java本地接口其它Java函數一樣運行(從調用該本地函數的Java程序的角度來看)。
這種方法有一個缺點:每次使用一個本地方法,若轉向一個新的系統必須移植這種方法。一個好的設計將用純粹的Java來完成邏輯及任務,而盡可能精簡JNI代碼。這樣可以充分復用項目間的本地函數。
中斷處理
存儲器存取的接口比較簡單,相比之下,中斷處理則復雜得多。對于起步者,有些OS甚至不讓他們訪問中斷。針對嵌入式和實時市場的OS要么在應用和內核空間之間不做任何區分,要么提供一個合適的API來處理用戶空間中斷(如QNX的Neutrino RTOS)。
即便操作系統提供了一個可通過JNI訪問的API,開發者仍需要解決JVM內的時序問題。如果JVM符合Java實時規范(RTSJ),所生成的實時線程不會被JVM垃圾收集器所阻礙。然而,如果沒有這些實時線程,由Java控制的任何中斷都要受垃圾收集器支配,不得不忍受數毫秒至數秒的延遲。如果這種延遲無法令人接受,那么必須采取基于JNI的解決方案。
假如純Java方案是可行的,就可以創建一個JNI容器類進行中斷管理。利用這種類就可啟動一個附著于所需中斷的高優先級線程,然后揭開該中斷,并等待處理循環中的中斷。之后,該線程就可讀或寫每個中斷上的硬件,并可以利用一個安全線程數據結構向系統內的低優先級線程發布任何數據。
對于有些系統,這種純Java方案可能無法提供所期望的性能和延遲。如果是這樣,就必須采用本地中斷處理機。在 RTOS.InterruptAttach()調用期間,相關的JNI代碼將創建一個線程或注冊一個處理機函數,然后將指針返回到堆積分配的ID數據結構。該代碼不調用RTOS.InterruptWait(),而是調用RTOS.InterruptPopData()和 RTOS.InterruptPushData()。當中斷開始時,本地線程可以從FIFO類型結構中讀取及寫入,為任何等待數據“彈出”的Java線程掃除障礙(如圖1所示)。
圖1:有時,純Java方案無法提供所期望的性能和延遲。如果是這樣,就必須采用本地中斷處理機。當中斷開始時,本地中斷處理機可以從FIFO類結構中讀取及寫入,為任何等待數據“彈出”的Java線程掃除障礙。
客戶端接口
如果設備驅動程序所處理的數據只能由它本身訪問,那么該驅動程序沒有太大用途。驅動程序需要與系統的其它部分相互通信。在 Unix等傳統操作系統上,一個客戶端程序可以通過打開一個設備的文件描述符來訪問該設備,一般是通過調用open()。如果該過程隨后繞過該描述符而執行一個系統調用,內核就將該調用路由到適當的設備。顯然,這種方法不適用于Java編寫的驅動程序。在一個良好的系統內,任何用戶應用程序都可能注冊一個設備并處理打開/讀取/寫入操作。盡管如此,這種方法并不總是容易移植。這迫使我們尋求兩種可能的路徑,即與客戶端-套接及內部JVM對話。
有些設備更加適合于采用Java的系統級工作。前端面板系統就是理想的候選對象。還可以利用Java廣泛的內置GUI API來設置一個仿真環境,以便在一個帶有按鈕小部件的窗口內運行被仿真的幀緩沖。在這種環境下,設計者可以在硬件到達之前設計、構建及調試應用程序驅動接口。
其它候選對象包括任何低中斷率、支持DMA的設備。這種設備在中斷之間有足夠的緩沖,從而可緩解JVM中的延遲問題。Java驅動程序不太適用的對象包括硬件緩沖器小且要求低延遲的高中斷率設備。