1 引言
VxWorks是美國Wind River Systems公司開發的一種嵌入式實時操作系統,它以高性能可剪裁的微內核以及友好的用戶開發環境在嵌入式實時操作系統領域佔有重要的地位。在很多實時應用場合(如嵌入式地理信息系統) ,需要在VxWorks環境下設計圖形用戶界面(GUI) 。VxWorks提供了兩種開發圖形用戶界面的組件:WindML (Wind多媒體庫)和Zinc。WindML為圖形、圖象和聲音應用程序的開發提供了基本技術,並且提供了為定製設備開發標準化驅動程序的框架。Zinc是以WindML為基礎的, Zinc是為應用創建圖形用戶界面提供框架的可擴展的面向對象的類庫。Zinc為實現複雜的圖形用戶界面提供了更多的支持。
為了提高性能和簡化源代碼,大多數情況下在一個實時環境中的應用程序是以多任務的方式實現的, Zinc自身通常也是多任務的。一般不在外部任務里直接處理GUI ,而是在一個專用的GUI任務里執行所有的GUI處理。在多任務環境下,非GUI任務將需要在不同的時期和GUI任務通信, GUI也可能需要與外部任務通信。GUI任務與非GUI任務之間的通信方式將對整個實時系統的實時性、可靠性和穩定性產生重大的影響。因此, 深入研究VxWorks環境下Zinc所支持的GUI任務與非GUI任務之間的通信方式並確定在特定應用場合選擇通信方式的原則是非常重要的。
2 GUI( Zinc)任務和外部任務之間的通信方法
GUI任務和非GUI任務之間的通信大致可以分為兩種:同步通信和非同步通信。Zinc主要提供了如下幾種通信機制供程序員選擇使用:
a)Zinc入口點
可以利用Zinc入口點實現GUI任務與非GUI任務之間的通信,主要有如下兩種方法:
b)共享內存
在VxWorks中很容易實現共享內存。為了安全地共享內存,最好讓一個信號量和共享內存關聯。這樣可以避免任務之間出現資源衝突。
使用共享內存的時候,通常不需要使用Zinc 提供的保證線程安全的入口點。但是當一個窗口對象的某個成員指向共享內存的時候是一個例外情況。例如,如果一個ZafButton的bitmapData成員正指向共享內存,除非已經確保Zinc任務目前沒有使用該共享內存,才能安全地更新該共享內存。可以使用ZafApplication::BeginSynchronize() 和ZafApplication::EndSynchronize() 函數來保證在某一時刻該任務是唯一使用該共享內存的任務。
c)OS消息隊列
在VxWorks環境下使用Zinc的時候,有兩種不同類型的消息隊列可供選用:事件管理器消息隊列和VxWorks操作系統提供的消息隊列。
可以通過ZafEventManager::Put()和ZafEventManager::Get()函數訪問事件管理器消息隊列。事件管理器隊列只提供從非GUI任務到Zinc任務的通信,利用事件管理器隊列進行從GUI任務到非GUI任務的通信是不實用的。
VxWorks消息隊列可以實現從GUI任務到非GUI任務的通信,也可以實現從非GUI任務到GUI任務的通信,但是不允許同時在兩個方向上進行通信。
d)派生設備
選用共享內存或消息隊列作為通信方法時,Zinc需要與該通信方法進行交互,這可以通過派生設備實現。使用派生設備的目的是為了檢查是否有來自另一個任務的通信。每當ZafEventManager::Get() 函數被調用,事件管理器輪詢該設備,看看是否有新消息。這個派生設備僅僅需要檢查共享內存或消息隊列。如果有新信息可用,派生設備可以直接調用對象的Event() 函數在隊列上面放置一個新事件,也可以自己處理這個消息。
派生設備還可用於實現從GUI任務到非GUI任務的通信。ZafEventManager::UnBlock()函數對這種通信方法是非常有用的。在正常的情況下,如果沒有需要處理的事件, Zinc會阻塞自己。如果採用一個派生設備監聽一個VxWorks消息隊列,向該隊列發送一個消息后解除事件管理器的阻塞可以更及時地輪詢該派生設備。派生設備自身不會阻塞,也不會導致Zinc暫停。
3 選擇通信方式的原則
上述關於GUI任務的通信方法各有其優缺點。在選擇通信方法的時候,應該以具體的應用場合為依據,一般應遵循如下的原則:
a) 應該儘可能選用簡單的通信方式。
在大多數情況下, Zinc入口點足夠用。Zinc入口點是最簡單的關於GUI任務的通信方式,因為它們不需要Zinc 任務內部的任何專門代碼。可用的最簡單入口點是ZafEventManager::Put()函數。然而,它有下列缺點:第一,它只允許從非GUI任務到GUI任務的通信;第二,它是非同步的;第三,因為要防止ZafEventManager::Get() 和ZafEventManager::Put()函數同時訪問Zinc事件隊列以對其進行保護, ZafEventManager::Put() 可能會阻塞。
如果非同步通信是可接受的,但是不能接受阻塞,可以採用下列兩種方法:第一,使用ZafEventManager::Put ( )函數,並且另外有一個可被阻塞的任務向Zinc隊列中放置事件。這個任務可以監聽一個OS消息隊列,而原先產生消息的任務正是使用OS消息隊列來發送消息; 第二,創建一個設備以監聽OS消息隊列,產生消息的任務發送一個消息給OS消息隊列,然後由派生設備接收並解釋。派生設備可以放置一個事件在Zinc隊列中,或者自己處理這個事件。只是這兩種方法都給應用程序增加了一點複雜性。
b) 如果需要進行同步通信,必須使用函數對ZafApplication::BeginSynchronize() 和ZafApplication::EndSynchronize() 。
調用ZafApplication::BeginSynchronize()之後,可以保證對Zinc對象的任何訪問是安全的。該方法很簡單,且不需要在GUI任務中添加專門的代碼。使用ZafApplication::BeginSynchronize() 的缺點是該函數會阻塞,使用該方法時必須採取預防措施。
c) 採用共享內存進行通信時必須創建保護和同步機制
共享內存是從GUI任務到非GUI任務的兩種通信方法之一,其優點是對數據的訪問簡單而直接。共享內存沒有對數據訪問進行保護的內在支持,所以必須創建一個對訪問進行保護及同步的機制,並且訪問共享內存的所有任務都應該使用該機制。採取這種方案的缺點是容易發生阻塞。
d) 在不能接受阻塞的應用場合,最好使用OS消息隊列。
OS消息隊列是從GUI任務到非GUI任務和從非GUI任務到GUI任務進行通信的另一種方法。使用OS消息隊列進行通信的時候,需要在GUI任務和非GUI任務中編寫訪問消息隊列的代碼。在正確進行設置的情況下,消息隊列不會引起阻塞的問題。創建消息隊列時,必須保證消息隊列有足夠的消息容量或者建立處理消息隊列溢出的機制。
4 Zinc的事件模型
Zinc中的GUI任務與非GUI任務的多種通信方式都與Zinc的事件模型有關,因此在設計和實現GUI任務與非GUI任務之間的通信時,需要對Zinc的事件模型有深入的理解。Zinc具有一個事件驅動的體系結構。輸入設備與應用程序之間的交互是通過事件完成的。由於VxWorks本身不是事件驅動的實時操作系統,在VxWorks運行平台中, Zinc主要從輸入設備和應用任務獲取事件。
然後Zinc以標準的方式將這些事件打包,並且將它們路由給適當的對象以進行進一步的處理。在EGIS中, GSM通訊任務使用了自定義的事件與GUI任務進行非同步通信。基於Zinc的EGIS事件模型如圖1所示。
圖1 基於Zinc的EGIS事件路由示意圖
從圖1中,在VxWorks中Zinc事件的主要來源是輸入設備和應用程序(比如GSM通信程序) ,由於VxWorks不支持事件驅動的系統,事件管理器周期性地查詢或接收來自輸入設備的數據並以Zinc定義的事件結構包裝成事件。一旦事件管理器獲得事件, Zinc主控進程重新獲得對應用程序的控制,該進程從時間管理器中得到事件並傳送給窗口管理器。窗口管理器決定事件的最終目的地和合適的路由並將其發送。最終窗口對象的Event方法收到每個事件並對其進行處理。在EGIS中,通過重載Event函數實現對自定義事件的處理。
5 EGIS 系統中GUI任務與非GUI任務之間通信的實現
嵌入式地理信息系統包括了兩個部分,跑車分系統和中心顯示分系統。跑車上包括一台PC機、一台GPS接收機和一台簡訊收發設備。中心包括一台VxWorks目標機、一台PC機(用於開發和調試)和一台簡訊收發設備。其中,VxWorks目標機上運行的是EGIS各功能模塊。
為了提高系統性能和簡化代碼,將目標機上的EGIS軟體劃分為兩個任務: GUI任務和GSM通信任務。GUI任務的主要功能是:負責界面和菜單的實現,並且需要根據GSM實時接收到的經緯度數據在地圖上畫出跑車的運行軌跡。GSM通訊任務的主要功能是:實時接收以短消息方式傳輸的GPS經緯度數據,存放在環形緩衝區中,並通知GUI任務。
EGIS系統需要滿足兩方面的實時性:第一, GSM通信任務接收經緯度數據的實時性;第二, GUI任務響應菜單操作的實時性和特定情況下畫出跑車軌跡的實時性。為此,在該系統中採取兩種通信方式: Zinc入口點和共享內存。GUI任務與非GUI任務之間通信的實現主要包括如下幾個方面:
a)用戶事件的定義
Zinc中的事件共分為七類,其中包括用戶事件。用戶事件的取值範圍為10,000到32,767。在EGIS中定義的用戶事件為:
const ZafEventType TRACKING = 10028;
其中, ZafEventType是Zinc事件類型。
b)用戶事件的發送
用戶事件的發送通過下面的調用完成:
zafApplication-> EventManager() -> Put(TRACKING)
其中, zafApplication是Zinc全局變數,也是GUI任務的任務變數。某些程序可能同時被多個任務調用,這些程序可能要求全局變數或靜態變數對於每個調用該程序的任務具有一個不同的值。為了適應這種情況, VxWorks提供了一種所謂任務變數的機制。一個4位元組的變數可以以任務變數的方式被添加到一個任務的上下文中,這樣每當任務切換的時候同時切換該變數的值。由於在GUI任務和GSM通信任務中的zafApplication具有不同的值,因此,可以在創建GSM通信任務的時候通過參數傳遞使GSM通信任務獲得GUI任務的任務變數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添加為任務變數*/
} /*通過參數傳遞GUI任務的任務變數zafApplication*/
在GSM通信任務中:
int Trace(CGIS_Window * pWindow,ZafApplication *application)
{
……
taskVarAdd(0,(int*)&zafApplication);
/*將zafApplication添加為任務變數*/
zafApplication = application; /*GSM通信任務變數zafApplication賦值*/
zafApplication->EventManager() -> Put(TRACKING) /*發送用戶事件*/
……
}
c)用戶事件的處理
ZafEventType CGIS_Window::Event(const ZafEventStruct& event)
{
ZafEventType code;
if ( event_type == TRACKING) /*判斷是否用戶定義事件TRACKING*/
{
……
m_pMainController->ZoomToPosition ( Lat_Long[0],Lat_Long[1]); /*事件處理函數*/
code = TrackCode;
return (code);
}
else
{
……/*處理其他事件*/
}
d)任務之間的數據共享
共享數據是通過下面的全局變數數據實現的。GSM任務收到數據之後,首先將經緯度數據存入數組Lat_Long中,然後將變數PntNumber加1,而GUI任務首先讀取PntNumber,然後處理Lat_Long中的數據。因此不會產生共享衝突。
float Lat_Long [ 2000 ]; /*存放通過GSM終端所收到的經緯度數據*/
int PntNumber; /*存放通過GSM終端所收到的經緯度數據的個數*/
6 結論
本文介紹了VxWorks支持的任務之間進行通信的各種方式,給出了在特定應用場合選擇任務之間通信方式的原則,介紹了Zinc的事件模型,並詳細地給出了嵌入式地理信息系統中非GUI任務與GUI任務進行通信的實現方式。實驗證明,該通信方式能夠滿足嵌入式地理信息系統各方面的實時性要求。
[admin via 研發互助社區 ] VxWorks環境下基於Zinc的GUI任務與非GUI任務之間的通信已經有2248次圍觀
http://cocdig.com/docs/show-post-42228.html