OAL時鐘與內核

Jason's Scribble OAL之系統時鐘 1. 系統時鐘與內核的關係 WinCE 5.0採用基於時間片的搶佔式多任務的實時內核,而且每個線程可以根據需要自行設定線程時間片的大小(參考CeSetThreadQuantum函數),默認爲100ms,這個默認值dwDefaultThreadQuantum也可以在OEMInit()時自行設定。在內核源文件中,與單詞Quantum有關的變量名一般是指時間片,WinCE內核定義了幾個與時鐘有關的全局變量,他們也是內核與OAL接口的一部分: 1) dwReschedTime,這個變量在內核的調度程序中更新,KCNextThread()是內核調度程序的一部分,它根據線程的優先級和狀態決定接下來處於運行狀態的線程,並根據該線程的時間片來設定dwReschedTime。顧名思義,這個變量就是指下一次運行調度程序的時間。 既然講的是WinCE 5.0的內核,這裏就不得不插入介紹一下它的調度程序有一個新特性叫做可變的系統時鐘節拍(system tick),目的就是讓硬件定時器(CPU的時鐘)只有在調度程序需要時才產生中斷。這樣不但可以提高調度程序的效率,而且有助於CPU節能(有關這點後面還會講到)。這個特性是可選的,依賴與OAL的實現。不採用這個特性,一般每個系統時鐘節拍爲1ms,即每毫秒產生一個時鐘中斷。 2) CurMSec,DWORD類型變量,每次時鐘中斷時更新,累計從系統啓動到當前一共經歷了多少系統時鐘節拍(太繞口了,或者說系統嘀噠(tick)了多少下)。 如果用戶程序調用GetTickCount()函數,其實就是直接返回這個變量的值。如上所述可知,當系統的時鐘節拍(system tick)爲1ms時,CurMSec就是毫秒級的計數器,GetTickCount()的精度就不會超過1ms。 在時鐘中斷處理函數(TimerISR)中除了要更新CurMSec,還要根據dwReschedTime來判斷是否到了運行調度程序的時間: if ((int)(CurMSec - dwReschedTime) >= 0) sysIntr = SYSINTR_RESCHED; 3) curridlehigh, curridlelow, idleconv 這幾個變量實現了一個64位的計數器,反映了系統處於空閒模式(Idle mode)的時間。一般在OEMIdle()函數內更新。用戶程序通過調用GetIdleTime()函數可以得到這個值。 這裏再插入一些與OEMIdle有關的知識點: 調度算法與電源管理有緊密的聯繫,也就是與現階段中央提倡的節能減排有一定的聯繫因爲絕大多數的移動設備都是希望節能的,所以當所有線程處於阻塞狀態時,內核就會通過調用OEMIdle()將CPU置於空閒模式(低功耗狀態)。這種狀態直到有中斷髮生或者有線程可運行時爲止。實際上,許多設備都是要花大量時間等待用戶的輸入,因此如果CPU支持低功耗模式,就應該在OEMIdle函數中實現之。 如果不採用前面提到的那個新的調度程序特性(Variable Tick Scheduler),在OEMIdle函數中還需要判斷Idle的準確時間段,而可變的系統時間節拍可以事先設置好正確的時鐘中斷,這樣OEMIdle函數就可以更快的將CPU置於低功耗狀態,這也就是說它節能的理由。 有時需要計算一段程序運行時的CPU佔用率,可以使用如下代碼: dwStartTick = GetTickCount(); dwIdleSt = GetIdleTime(); Sleep(); // 被測代碼段 dwStopTick = GetTickCount(); dwIdleEd = GetIdleTime(); PercentIdle = ((100*(dwIdleEd - dwIdleSt)) / (dwStopTick - dwStartTick)); 這段代碼見MSDN中GetIdleTime一節,1-PercentIdle即爲CPU佔用率。 2. 系統時鐘模塊結構 這篇文章是以PQOAL(Production-Quality OAL)結構介紹系統時鐘模塊的實現,PQOAL是WinCE 5.0開始纔有的東東,它可以簡化OAL代碼的實現,並有利於OAL代碼的移植。簡單分析一下目錄結構: %_WINCEROOT%/Platform/Common 爲硬件無關以及CPU相關的PQOAL代碼; %_WINCEROOT%/Platform/Common/Src/Common 爲硬件無關的代碼,下面以黃色背景列舉這類函數; %_WINCEROOT%/Platform/Common/Src/$CPU$ 爲CPU相關代碼,下面以綠色背景列舉這類函數。 如果你的平臺所採用的CPU在這個目錄中已經存在,比如pxa270相關代碼就在%_WINCEROOT%/Platform/Common/Src/ARM/Intel/PXA27x目錄下,一般情況下不需要做任何修改,這些代碼就可以完成大部分OAL的功能。單就pxa270的系統時鐘模塊而言,不需要自己寫任何代碼也可以使用,但這部分代碼其實沒有實現WinCE 5.0纔有的那個新特性(Variable Tick Scheduler)。尤其是OALCPUIdle()的默認實現只是簡單的循環,最好能自行實現這個函數,使CPU置於低功耗狀態。 在OAL中,與系統時鐘相關的數據結構,變量和函數接口在Oal_timer.h頭文件中都有其定義或者註釋。 (%_WINCEROOT%/Platform/Common/Src/Inc/Oal_timer.h) 2.1 與內核進行交互的變量和函數 2.1.1 UINT32 OALTimerIntrHandler(); 這個就是前面提到的TimerISR,即時鐘中斷處理函數,返回一個SYSINTR_的值。 2.1.2 CurMSec,前面已經提到,定義在內核源文件中。 2.1.3 OEMIdle(),前面已經提到,已有PQOAL實現,其內部會調用OALCPUIdle()將CPU置於空閒模式(低功耗狀態)。 2.1.4 SC_GetTickCount() 這個是內核需要的毫秒級定時器,返回從系統啓動開始算起的毫秒數。如果系統時鐘節拍爲1ms,那麼直接返回CurMSec即可,否則如果系統時鐘節拍超過1ms,則需要高精度計數器來調整到一個毫秒的時間間隔。 該函數已經由PQOAL實現,但它會調用與硬件相關的函數OALTimerCountsSinceSysTick()。 2.1.5 pQueryPerformanceFrequency, pQueryPerformanceCounter 內核中定義的函數指針,通過這兩個函數實現高精度的計時器。 這兩個函數的原型也已經由PQOAL實現,即OALTimerQueryPerformanceFrequency和OALTimerQueryPerformanceCounter,也會調用與硬件相關的函數OALTimerCountsSinceSysTick()和OAL層變量g_oalTimer。 2.1.6 pOEMUpdateRescheduleTime 內核中定義的函數指針,如果要採用Variable Tick Scheduler就有必要實現這個函數,主要是根據變化了的系統節拍(tick)重新設置時鐘的定時週期,從而避免不必要的時鐘中斷。 PQOAL提供了OALTimerUpdateRescheduleTime函數的例子可以參考實現,它會調用與硬件相關的函數OALTimerUpdate()。 2.2 與其他OAL模塊交互的變量和函數 2.2.1 OALTimerInit() 這個函數用來初始化系統時鐘,通常是在OEMInit函數中調用。具體包括初始化OAL層變量g_oalTimer,初始化內核變量curridlehigh, curridlelow, idleconv,初始化內核函數指針(2.1.5,2.1.6),初始化硬件時鐘定時器。 2.2.2 g_oalTimer,OAL_TIMER_STATE結構的變量,包含各種系統時鐘相關的變量。 typedef struct { UINT32 countsPerMSec; // counts per 1 msec UINT32 countsMargin; // counts margin UINT32 maxPeriodMSec; // maximal timer period in MSec UINT32 msecPerSysTick; // msec per system tick UINT32 countsPerSysTick; // counts per system tick UINT32 actualMSecPerSysTick; // actual msec per system tick UINT32 actualCountsPerSysTick; // actual counts per system tick volatile UINT64 curCounts; // counts at last system tick } OAL_TIMER_STATE, *POAL_TIMER_STATE; 2.2.3 OALTimerCountsSinceSysTick() 這個函數使用高精度計數器返回從上一次系統嘀噠後經過的時間,一般需要精度高於1ms,換句話說,硬件時鐘的晶振頻率要高於1MHz。 2.2.4 OALTimerUpdateRescheduleTime(),參見2.1.6 2.2.5 OALTimerRecharge(),OALTimerUpdate(),OALStall() 2.2.6 OALGetTickCount() 3. 實現高精度定時器 有時用戶程序希望調用QueryPerformanceCounter()和QueryPerformanceFrequency()實現精度高於1ms的計時。按照2.1.5所述,PQOAL已經基本實現了這部分代碼,我們只需根據所採用的CPU實現OALTimerCountsSinceSysTick(2.2.3)。 PXA270處理器有兩組時鐘通道,允許通過軟件設置時鐘中斷,他們被稱作操作系統時鐘(Operating System Timer),詳細內容請參考《Intel PXA27x Processor Family Developer's Manual》。 這裏只介紹一組與PXA255兼容的時鐘通道,它採用3.25M晶振,即每毫秒可以計數3250次,有了它就可以實現精度高於1ms的計時器。 INT32 OALTimerCountsSinceSysTick() { UINT32 LastTimerMatch = (g_XllpOSTHandle.pOSTRegs->osmr0 - g_oalTimer.countsPerSysTick); // How many ticks since the last timer interrupt? return (g_XllpOSTHandle.pOSTRegs->oscr0 - LastTimerMatch); } 原文網址:http://www.cnblogs.com/jasonye/archive/2007/06/27/796756.html
發佈了16 篇原創文章 · 獲贊 0 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章