VxWorks是美國Wind River Systems公司開發(fā)的一種嵌入式實時操作系統(tǒng),它以高性能可剪裁的微內(nèi)核以及友好的用戶開發(fā)環(huán)境在嵌入式實時操作系統(tǒng)領(lǐng)域占有重要的地位。
在很多實時應(yīng)用場合(如嵌入式地理信息系統(tǒng)) ,需要在VxWorks環(huán)境下設(shè)計圖形用戶界面(GUI) 。VxWorks提供了兩種開發(fā)圖形用戶界面的組件:WindML (Wind多媒體庫)和Zinc。WindML為圖形、圖象和聲音應(yīng)用程序的開發(fā)提供了基本技術(shù),并且提供了為定制設(shè)備開發(fā)標(biāo)準(zhǔn)化驅(qū)動程序的框架。Zinc是以WindML為基礎(chǔ)的, Zinc是為應(yīng)用創(chuàng)建圖形用戶界面提供框架的可擴(kuò)展的面向?qū)ο蟮念悗?。Zinc為實現(xiàn)復(fù)雜的圖形用戶界面提供了更多的支持。
為了提高性能和簡化源代碼,大多數(shù)情況下在一個實時環(huán)境中的應(yīng)用程序是以多任務(wù)的方式實現(xiàn)的, Zinc自身通常也是多任務(wù)的。一般不在外部任務(wù)里直接處理GUI ,而是在一個專用的GUI任務(wù)里執(zhí)行所有的GUI處理。在多任務(wù)環(huán)境下,非GUI任務(wù)將需要在不同的時期和GUI任務(wù)通信, GUI也可能需要與外部任務(wù)通信。GUI任務(wù)與非GUI任務(wù)之間的通信方式將對整個實時系統(tǒng)的實時性、可靠性和穩(wěn)定性產(chǎn)生重大的影響。因此, 深入研究VxWorks環(huán)境下Zinc所支持的GUI任務(wù)與非GUI任務(wù)之間的通信方式并確定在特定應(yīng)用場合選擇通信方式的原則是非常重要的。
2 GUI( Zinc)任務(wù)和外部任務(wù)之間的通信方法
GUI任務(wù)和非GUI任務(wù)之間的通信大致可以分為兩種:同步通信和異步通信。Zinc主要提供了如下幾種通信機(jī)制供程序員選擇使用:
Zinc入口點(diǎn)
共享內(nèi)存
OS消息隊列
派生設(shè)備
a)Zinc入口點(diǎn)
可以利用Zinc入口點(diǎn)實現(xiàn)GUI任務(wù)與非GUI任務(wù)之間的通信,主要有如下兩種方法:
與Zinc通信的基本入口點(diǎn)是ZafEventManager::Put()程序。利用該函數(shù)可以在Zinc事件隊列中放置一個事件。這是一個異步方法,因此只能適用于可接受異步通信的場合。
ZafApplication::BeginSynchronize() 和ZafApplication::EndSynchronize() 。這兩個函數(shù)自身并不是通信程序,但能確保直接通信方式訪問Zinc是安全的。直接通信通常由一個對象的Event()函數(shù)來實現(xiàn),也可能采用其它的方式,比如數(shù)據(jù)對象更新。間接通信可以使用ZafEventManager::Unblock()函數(shù)來實現(xiàn)。使用一個派生設(shè)備或其它方法進(jìn)行通信的時候,使用該函數(shù)可以使正在等待事件的Zinc任務(wù)解除阻塞。
b)共享內(nèi)存
在VxWorks中很容易實現(xiàn)共享內(nèi)存。為了安全地共享內(nèi)存,讓一個信號量和共享內(nèi)存關(guān)聯(lián)。這樣可以避免任務(wù)之間出現(xiàn)資源沖突。
使用共享內(nèi)存的時候,通常不需要使用Zinc 提供的保證線程安全的入口點(diǎn)。但是當(dāng)一個窗口對象的某個成員指向共享內(nèi)存的時候是一個例外情況。例如,如果一個ZafButton的bitmapData成員正指向共享內(nèi)存,除非已經(jīng)確保Zinc任務(wù)目前沒有使用該共享內(nèi)存,才能安全地更新該共享內(nèi)存。可以使用ZafApplication::BeginSynchronize() 和ZafApplication::EndSynchronize() 函數(shù)來保證在某一時刻該任務(wù)是使用該共享內(nèi)存的任務(wù)。
c)OS消息隊列
在VxWorks環(huán)境下使用Zinc的時候,有兩種不同類型的消息隊列可供選用:事件管理器消息隊列和VxWorks操作系統(tǒng)提供的消息隊列。
可以通過ZafEventManager::Put()和ZafEventManager::Get()函數(shù)訪問事件管理器消息隊列。事件管理器隊列只提供從非GUI任務(wù)到Zinc任務(wù)的通信,利用事件管理器隊列進(jìn)行從GUI任務(wù)到非GUI任務(wù)的通信是不實用的。
VxWorks消息隊列可以實現(xiàn)從GUI任務(wù)到非GUI任務(wù)的通信,也可以實現(xiàn)從非GUI任務(wù)到GUI任務(wù)的通信,但是不允許同時在兩個方向上進(jìn)行通信。
d)派生設(shè)備
選用共享內(nèi)存或消息隊列作為通信方法時,Zinc需要與該通信方法進(jìn)行交互,這可以通過派生設(shè)備實現(xiàn)。使用派生設(shè)備的目的是為了檢查是否有來自另一個任務(wù)的通信。每當(dāng)ZafEventManager::Get() 函數(shù)被調(diào)用,事件管理器輪詢該設(shè)備,看看是否有新消息。這個派生設(shè)備僅僅需要檢查共享內(nèi)存或消息隊列。如果有新信息可用,派生設(shè)備可以直接調(diào)用對象的Event() 函數(shù)在隊列上面放置一個新事件,也可以自己處理這個消息。
派生設(shè)備還可用于實現(xiàn)從GUI任務(wù)到非GUI任務(wù)的通信。
ZafEventManager::UnBlock()函數(shù)對這種通信方法是非常有用的。在正常的情況下,如果沒有需要處理的事件, Zinc會阻塞自己。如果采用一個派生設(shè)備監(jiān)聽一個VxWorks消息隊列,向該隊列發(fā)送一個消息后解除事件管理器的阻塞可以更及時地輪詢該派生設(shè)備。派生設(shè)備自身不會阻塞,也不會導(dǎo)致Zinc暫停。
3 選擇通信方式的原則
上述關(guān)于GUI任務(wù)的通信方法各有其優(yōu)缺點(diǎn)。在選擇通信方法的時候,應(yīng)該以具體的應(yīng)用場合為依據(jù),一般應(yīng)遵循如下的原則:
a) 應(yīng)該盡可能選用簡單的通信方式。
在大多數(shù)情況下, Zinc入口點(diǎn)足夠用。Zinc入口點(diǎn)是簡單的關(guān)于GUI任務(wù)的通信方式,因為它們不需要Zinc 任務(wù)內(nèi)部的任何專門代碼??捎玫暮唵稳肟邳c(diǎn)是ZafEventManager::Put()函數(shù)。然而,它有下列缺點(diǎn):,它只允許從非GUI任務(wù)到GUI任務(wù)的通信;第二,它是異步的;第三,因為要防止ZafEventManager::Get() 和ZafEventManager::Put()函數(shù)同時訪問Zinc事件隊列以對其進(jìn)行保護(hù), ZafEventManager::Put() 可能會阻塞。
如果異步通信是可接受的,但是不能接受阻塞,可以采用下列兩種方法:,使用ZafEventManager::Put ( )函數(shù),并且另外有一個可被阻塞的任務(wù)向Zinc隊列中放置事件。這個任務(wù)可以監(jiān)聽一個OS消息隊列,而原先產(chǎn)生消息的任務(wù)正是使用OS消息隊列來發(fā)送消息; 第二,創(chuàng)建一個設(shè)備以監(jiān)聽OS消息隊列,產(chǎn)生消息的任務(wù)發(fā)送一個消息給OS消息隊列,然后由派生設(shè)備接收并解釋。派生設(shè)備可以放置一個事件在Zinc隊列中,或者自己處理這個事件。只是這兩種方法都給應(yīng)用程序增加了一點(diǎn)復(fù)雜性。
b) 如果需要進(jìn)行同步通信,必須使用函數(shù)對ZafApplication::BeginSynchronize() 和ZafApplication::EndSynchronize() 。
調(diào)用ZafApplication::BeginSynchronize()之后,可以保證對Zinc對象的任何訪問是安全的。該方法很簡單,且不需要在GUI任務(wù)中添加專門的代碼。使用ZafApplication::BeginSynchronize() 的缺點(diǎn)是該函數(shù)會阻塞,使用該方法時必須采取預(yù)防措施。
c) 采用共享內(nèi)存進(jìn)行通信時必須創(chuàng)建保護(hù)和同步機(jī)制
共享內(nèi)存是從GUI任務(wù)到非GUI任務(wù)的兩種通信方法之一,其優(yōu)點(diǎn)是對數(shù)據(jù)的訪問簡單而直接。共享內(nèi)存沒有對數(shù)據(jù)訪問進(jìn)行保護(hù)的內(nèi)在支持,所以必須創(chuàng)建一個對訪問進(jìn)行保護(hù)及同步的機(jī)制,并且訪問共享內(nèi)存的所有任務(wù)都應(yīng)該使用該機(jī)制。采取這種方案的缺點(diǎn)是容易發(fā)生阻塞。
d) 在不能接受阻塞的應(yīng)用場合,使用OS消息隊列。
OS消息隊列是從GUI任務(wù)到非GUI任務(wù)和從非GUI任務(wù)到GUI任務(wù)進(jìn)行通信的另一種方法。使用OS消息隊列進(jìn)行通信的時候,需要在GUI任務(wù)和非GUI任務(wù)中編寫訪問消息隊列的代碼。在正確進(jìn)行設(shè)置的情況下,消息隊列不會引起阻塞的問題。創(chuàng)建消息隊列時,必須保證消息隊列有足夠的消息容量或者建立處理消息隊列溢出的機(jī)制。
4 Zinc的事件模型
Zinc中的GUI任務(wù)與非GUI任務(wù)的多種通信方式都與Zinc的事件模型有關(guān),因此在設(shè)計和實現(xiàn)GUI任務(wù)與非GUI任務(wù)之間的通信時,需要對Zinc的事件模型有深入的理解。Zinc具有一個事件驅(qū)動的體系結(jié)構(gòu)。輸入設(shè)備與應(yīng)用程序之間的交互是通過事件完成的。由于VxWorks本身不是事件驅(qū)動的實時操作系統(tǒng),在VxWorks運(yùn)行平臺中, Zinc主要從輸入設(shè)備和應(yīng)用任務(wù)獲取事件。
然后Zinc以標(biāo)準(zhǔn)的方式將這些事件打包,并且將它們路由給適當(dāng)?shù)膶ο笠赃M(jìn)行進(jìn)一步的處理。在EGIS中, GSM通訊任務(wù)使用了自定義的事件與GUI任務(wù)進(jìn)行異步通信?;赯inc的EGIS事件模型如圖1所示。

圖1 基于Zinc的EGIS事件路由示意圖
從圖1中,在VxWorks中Zinc事件的主要來源是輸入設(shè)備和應(yīng)用程序(比如GSM通信程序) ,由于VxWorks不支持事件驅(qū)動的系統(tǒng),事件管理器周期性地查詢或接收來自輸入設(shè)備的數(shù)據(jù)并以Zinc定義的事件結(jié)構(gòu)包裝成事件。一旦事件管理器獲得事件, Zinc主控進(jìn)程重新獲得對應(yīng)用程序的控制,該進(jìn)程從時間管理器中得到事件并傳送給窗口管理器。窗口管理器決定事件的終目的地和合適的路由并將其發(fā)送。終窗口對象的Event方法收到每個事件并對其進(jìn)行處理。在EGIS中,通過重載Event函數(shù)實現(xiàn)對自定義事件的處理。
5 EGIS 系統(tǒng)中GUI任務(wù)與非GUI任務(wù)之間通信的實現(xiàn)
嵌入式地理信息系統(tǒng)包括了兩個部分,跑車分系統(tǒng)和中心顯示分系統(tǒng)。跑車上包括一臺PC機(jī)、一臺GPS接收機(jī)和一臺短信收發(fā)設(shè)備。中心包括一臺VxWorks目標(biāo)機(jī)、一臺PC機(jī)(用于開發(fā)和調(diào)試)和一臺短信收發(fā)設(shè)備。其中,VxWorks目標(biāo)機(jī)上運(yùn)行的是EGIS各功能模塊。
為了提高系統(tǒng)性能和簡化代碼,將目標(biāo)機(jī)上的EGIS軟件劃分為兩個任務(wù): GUI任務(wù)和GSM通信任務(wù)。GUI任務(wù)的主要功能是:負(fù)責(zé)界面和菜單的實現(xiàn),并且需要根據(jù)GSM實時接收到的經(jīng)緯度數(shù)據(jù)在地圖上畫出跑車的運(yùn)行軌跡。GSM通訊任務(wù)的主要功能是:實時接收以短消息方式傳輸?shù)腉PS經(jīng)緯度數(shù)據(jù),存放在環(huán)形緩沖區(qū)中,并通知GUI任務(wù)。
EGIS系統(tǒng)需要滿足兩方面的實時性:, GSM通信任務(wù)接收經(jīng)緯度數(shù)據(jù)的實時性;第二, GUI任務(wù)響應(yīng)菜單操作的實時性和特定情況下畫出跑車軌跡的實時性。為此,在該系統(tǒng)中采取兩種通信方式: Zinc入口點(diǎn)和共享內(nèi)存。GUI任務(wù)與非GUI任務(wù)之間通信的實現(xiàn)主要包括如下幾個方面:
a)用戶事件的定義
Zinc中的事件共分為七類,其中包括用戶事件。用戶事件的取值范圍為10,000到32,767。在EGIS中定義的用戶事件為:
const ZafEventType TRACKING = 10028;
其中, ZafEventType是Zinc事件類型。
b)用戶事件的發(fā)送
用戶事件的發(fā)送通過下面的調(diào)用完成:
zafApplication-> EventManager() -> Put(TRACKING)
其中, zafApplication是Zinc全局變量,也是GUI任務(wù)的任務(wù)變量。某些程序可能同時被多個任務(wù)調(diào)用,這些程序可能要求全局變量或靜態(tài)變量對于每個調(diào)用該程序的任務(wù)具有一個不同的值。為了適應(yīng)這種情況, VxWorks提供了一種所謂任務(wù)變量的機(jī)制。一個4字節(jié)的變量可以以任務(wù)變量的方式被添加到一個任務(wù)的上下文中,這樣每當(dāng)任務(wù)切換的時候同時切換該變量的值。由于在GUI任務(wù)和GSM通信任務(wù)中的zafApplication具有不同的值,因此,可以在創(chuàng)建GSM通信任務(wù)的時候通過參數(shù)傳遞使GSM通信任務(wù)獲得GUI任務(wù)的任務(wù)變量zafApplication:
if (GSMTaskID = taskSpawn("SERIAL_PORT",90, VX_FP_TASK,
ZAF_VXW_STACK_SIZE, (FUNCPTR)Trace,
(int)zafApplication,0,0,0,0,0,0,0,0,0) != ERROR)
{
taskVarAdd(GSMTaskID,(int*)
&zafApplication);
/*將zafApplication添加為任務(wù)變量*/
} /*通過參數(shù)傳遞GUI任務(wù)的任務(wù)變量zafApplication*/
在GSM通信任務(wù)中:
int Trace(CGIS_Window * pWindow,ZafApplication *application)
{
……
taskVarAdd(0,(int*)&zafApplication);
/*將zafApplication添加為任務(wù)變量*/
zafApplication = application; /*GSM通信任務(wù)變量zafApplication賦值*/
zafApplication->EventManager() -> Put(TRACKING) /*發(fā)送用戶事件*/
……
}
c)用戶事件的處理
ZafEventType CGIS_Window::Event(const ZafEventStruct& event)
{
ZafEventType code;
if ( event_type == TRACKING) /*判斷是否用戶定義事件TRACKING*/
{
……
m_pMainController->ZoomToPosition ( Lat_Long[0],Lat_Long[1]); /*事件處理函數(shù)*/
code = TrackCode;
return (code);
}
else
{
……/*處理其他事件*/
}
d)任務(wù)之間的數(shù)據(jù)共享
共享數(shù)據(jù)是通過下面的全局變量數(shù)據(jù)實現(xiàn)的。GSM任務(wù)收到數(shù)據(jù)之后,首先將經(jīng)緯度數(shù)據(jù)存入數(shù)組Lat_Long中,然后將變量PntNumber加1,而GUI任務(wù)首先讀取PntNumber,然后處理Lat_Long中的數(shù)據(jù)。因此不會產(chǎn)生共享沖突。
float Lat_Long [ 2000 ]; /*存放通過GSM終端所收到的經(jīng)緯度數(shù)據(jù)*/
int PntNumber; /*存放通過GSM終端所收到的經(jīng)緯度數(shù)據(jù)的個數(shù)*/
6 結(jié)論
本文介紹了VxWorks支持的任務(wù)之間進(jìn)行通信的各種方式,給出了在特定應(yīng)用場合選擇任務(wù)之間通信方式的原則,介紹了Zinc的事件模型,并詳細(xì)地給出了嵌入式地理信息系統(tǒng)中非GUI任務(wù)與GUI任務(wù)進(jìn)行通信的實現(xiàn)方式。實驗證明,該通信方式能夠滿足嵌入式地理信息系統(tǒng)各方面的實時性要求。