摘要:在進行網絡通信設備開發時,需要使用通用定時器。本文在研究VxWorks系統看門狗函數的基礎上,提出了基于看門狗函數的定制定時器的設計方法,可以使定制定時器的小周期滿足網絡通信設備開發的需要 本文給出了定制定時器實現的思想。
1 概述
VxWorks是WindRiver公司開發的具有工業領導地位的高性能實時操作系統內核,具有先進的網絡功能,易于設計高效的嵌入式系統 目前已成為嵌入式操作系統的,并將其作為通信產品的軟件平臺。
在進行網絡通信設備開發時,需要用到定時器,如以周期為m秒對某個端口進行查詢、以周期為n分對某個設備的性能數據進行統計等。而VxWorks只提供了像watchDog(看門狗),而沒有提供一個通用的定時器。本文提出的通用定時器設計方法,占用系統資源少,運行效率高,并可根據需要定制滿足要求的定時器。
2 通用定時器的基本原理
雖然在Vxworks沒有提供像windows中的定時器一樣好用的定時器,但可以利用VxWorks的看門狗來實現定時器。對看門狗的操作函數主要有三個:創建看門狗函數WDOG_ID wdCreate(void);啟動看門狗函數STATUS wdStart(WDOG_ID wdId,int delay,FUNCPTR pRoutine,int parameter);刪除看門狗函數STATUS wdDelete(WDOG_ID wdId)。
通過對看門狗啟動函數進行研究發現,如果能夠在看門狗的響應函數中重新啟動看門狗.那么就可以實現以一個固定的周期循環執行的定時器。以下是定時器的基本框架:
void settimer(int interval)
{ int in_interval;
m_interval=sysClkRateGet()*interval;/*將延時秒數轉化為ticks數*/
gm wdID =wdCreate();/*創建看門狗*/
if(gm_perfHist_wdID = NULL)
{
printf("Could not create watchdog timer\n");
return ;
}
if(wdStart(gm_wdID,m_interval,(FUNCPTR)
action_func,interval) = ERROR)
/*啟動看門狗*/
{
printf ("Error in starting routine associated with timer\n");
wdDelete(gm_wdlD);/*如果啟動看門狗不成功,則刪除看門狗*/
return;
}
return;
}
void action_func(int interval)
{ int m_interval;
m_interval=sysClkRateGet()*interval;/*將延時秒數轉化為ticks數*/
if(wdStart(gm_wdID,m_interval,(FUNCPTR)action_fnnc,interval)= ERROR)
/*啟動看門狗*/
{
printf ("Error in starting routine associated with timer\n");
wdDelete(gm_wdID);
return;
}
/*執行用戶函數*/
……
}
用戶只需調用settimer并給出以秒為單位的定時器周期,就可以建立一個定時器。
3 通用定時器設計
通過調用settimer可實現一個基本的定時器,但在程序中對定時器的要求比較復雜。如有可能在運行過程中,要求加人某個需用輪詢的函數,這樣就要建立一個較為通用的定時器。
3.1 通用定時器的設計思想
通用定時器應能滿足用戶的要求.要能動態地將用戶需輪詢函數加入到定時器的輪詢隊列中。要實現動態加載用戶需輪詢函數,可將用戶需輪詢函數串接成一個鏈表(鏈表的每個結點稱為一個定時器用戶)。在每個定時器周期到期時,遍歷定時器用戶鏈表,執行用戶函數,這樣就可以實現動態加載用戶需輪詢函數。
如果輪詢周期與定時器周期相同,那么加入的用戶輪詢函數,在次執行時,其周期將會小于所要求的輪詢周期,在壞情況下,可能相差近一個周期。要解決這個問題可將定時器的周期設置為較小,且在定時器用戶結點中記錄用戶的輪詢周期和距離輪詢周期到期所剩余時間,在定時器到期時,用剩余時間減去定時器周期,如果結果小于零,就執行用戶函數。用這種方法可以解決次執行時的時差較大問題;另一方面,動態加載的用戶函數可以以不同的周期進行輪詢。
另一個問題是在定時器到期時遍歷定時器用戶鏈表并執行相關用戶函數時的效率:如果用戶函數執行時間較長(如板間通信),由于函數的執行會影響鏈表下一個結點函數的執行。為解決這個問題可以為每個用戶函數啟動一個任務。但反復地為用戶創建、刪除任務,會加重系統的開銷。解決這個問題可以在創建定時器用戶時,就為定時器用戶的響應函數創建一個任務,并將此任務掛起。當要求的輪詢周期到期時,喚醒此任務;為了能夠正確地對此任務進行操作,就要在定時器用戶結點的數據結構中增加一個關聯任務標識。在創建定時器用戶時,由用戶指明是否為其響應函數創建關聯任務。這樣,在定時器遍歷定時器用戶鏈表時,如果檢測到關聯任務為無效值時,就直接執行用戶函數,而不創建新的任務。
根據通用定時器的設計思想,可以設計出通用定時器結構如附圖所示:
系統初始化時,將設置并啟動一個默認的周期為1秒的定時器0,此定時可以滿足大部分用戶的要求;如果此定時器不能滿足用戶的要求,也可以定制自己的定時器,在不需要使用定制的定時器時,要將其刪除,否則由于定時器要占用一定的系統資源,而造成系統資源的浪費。但系統默認定時器0是無法刪除的。
用戶可以將需要以一定周期運行的函數加入到符合要求的定時器的用戶隊列中,這樣用戶的函數就可以以一定周期運行 當用戶認為已經加入定時器用戶隊列的函數,不需要再周期性運行時,可以將其從隊列中刪除。
3.3 通用定時器的數據結構
通用定時器的數據結構分為兩部分,即定時器數據結構和定時器用戶數據結構。
定時器數據結構:描述定時器的信息。包括用于標識此定時器的ID、用于分配定時器用戶ID的有效索引值、定時器的周期、定時器所用的看門狗ID以及定時器用戶隊列。
定時器用戶數據結構:描述每個加入定時器的用戶的信息。主要有定時器用戶ID、用戶指定的輪詢周期、距離用戶指定周期到期剩余時間、用戶響應函數及響應函數所帶的參數以及參數長度。為了方便不同用戶使用,應將響應函數所帶參數設置為無符號指針,這樣用戶就可以設置任何一種數據類型。如果用戶參數較多,可以采用結構指針。
3.4 通用定時器的具體實現
通用定時器的實現主要包括下面幾類函數:
①定時器核心實現函數:將定時器的實現框架進行擴充,使其能夠以較小的周期運行。當一周期到期時,依次遍歷定時器的用戶鏈表,將每個定時器用戶結點的剩余時間域減去定時器周期,如果小于零,說明用戶指定的周期已經到期,則執行用戶的響應函數。如果此結點的關聯任務ID域為一個有效值,則喚醒相應的任務,否則直接執行用戶響應函數。
② 定時器初始化函數:主要完成的操作是首先設置一個全局的定時器鏈表針,建立一個系統默認定時器0,并啟動這個默認定時器,在用戶開始使用定時器時,一定要對定時器系統進行初始化。
③創建定制定時器函數:在系統初始化后默認創建了一個的定時器,但這個定時器可能不能完全滿足用戶的要求,有的可能要求運行其響應函數的時差精度小于1秒,因此定制定時器的周期允許小于1秒。考慮到VxWorks中的實際情況,這里規定定時的小周期為0.1秒。wxWorks中的看門狗是以click為時間單位,1秒等于60個click。因此.如果要求的周期太小,導致定時器占用的CPU時間過長而降低系統性能。
要注意的是在vxWorks中不提供強制類型轉換,所以,在使用此函數時,如果輸入參數為整數則應以小數形式給出,例如:參數為15,則要輸入15.0。
④ 刪除定制定時器函數:當用戶不再使用定制的定時器時,要刪除自己創建的定制定時器。輸入的參數為已經存在的定時器的ID。
⑤ 創建定時器用戶函數:要將一個需輪詢的用戶函數動態地加入到一個符臺要求的定時器中,必須在這個定時器的定時器用戶鏈表中為這個用戶函數創建一個相應的結點,這樣就可以按用戶要求的周期去周期性地調用用戶響應函數。根據通用定時器設計,如果用戶響應函數執行時間較長則要為其創建一個任務。創建定時器用戶時,需要指定以下參數:
timerID:定時器ID,如果使用默認定時器,則設置為0;如果使用用戶定制定時器,則要填入由定制定時器函數返回的定時器ID。
interval:用戶要求的運行周期;
actionFunc:用戶要求周期性運行的函數;
param:用戶函數的參數指針;
pgram_fen:用戶函數參數的長度;
createTask:是否為用戶響應函數創建一個任務,1:創建,0:不創建。
⑥ 刪除定時器用戶函數:當用戶不再需要對定期輪詢某個函數時,要將其從定時器用戶鏈表中刪除。
4 定時器的應用
在使用通用定時器之前,要先初始化定時器系統,這樣就會在系統中,生成一個周期為1秒的定時器,如果用戶要求的輪詢時差精度大于1秒,則可將自己要輪詢的函數,用創建定時器用戶函數加入到默認定時器的定時器用戶鏈表中,如果要求系統創建任務則將尾一個參數設為1。
如果系統默認的定時器0精度不能滿足其要求,則用戶可以用創建定制定時器函數創建一個定制的定時器。當此定時器不再需要時,要將它刪除。
本文設計的這個通用定時器,已經應用于一個實際的網絡設備中,其運行平穩.占用系統資源少,且可以滿足各種不同用戶的需要。