Windows Embedded CE
中斷結構分析
關鍵字:WinCE,中斷,體系,結構
摘要:本文主要以WinCE .NET 5.0
爲操作系統平臺,ARM爲硬件平臺,分析了WinCE下
中斷的結構與實現方式
作者:ETDrawer@ARMCE
前言
在嵌入式系統當中,對於中斷的處理是非常重要的一部分內容。許多外圍設備都需要通
過中斷來實現自己的功能或者與系統內核交互,系統時鐘本身也是由時鐘中斷產生的。所以
本文旨在分析WinCE下的中斷的結構,以及常用的幾種實現方式,來幫助讀者瞭解WinCE
中斷體系及實現自己的中斷處理結構。
下面的介紹如非特殊說明,均以 ARM
架構爲硬件基礎,操作系統代碼使用 Windows
embedded CE 5.0。
一 WinCE中斷體系結構
先看圖 1:
圖 1 WinCE中斷體系結構
這張是經典的說明中斷體系的圖,我們可以通過分析這張圖來了解WinCE的中斷體系。
從結構上看,WinCE中斷涉及4
層,即:硬件層、內核層、OAL層、IST處理層。了
解這 4層之間的交互傳遞將對我們瞭解WinCE中斷處理很有幫助。
1
硬件層:
硬件層就是實際觸發中斷的硬件,這裏主要有兩方面作用,一個是觸發中斷,第二個是
enable/disable硬件中斷。
2
內核層:
這一層由內核來處理,包括中斷異常產生後跳轉到相應的ISR,以及根據SYSINTR
來
觸發相應的Event。關於SYSINTR
和 IRQ
的概念後面會說明。
3 OAL層
這一層主要就是我們需要實現的代碼了,來識別硬件IRQ,對應到SYSINTR。
4 IST處理層
一般使用 IST
來做實際的中斷處理,這樣不會佔用很多的鎖定系統時間來處理中斷,
但是對中斷的實時性大打折扣
二 IRQ,ISR,IST和 SYSINTR
說到這裏先解釋下IRQ,ISR,IST及 SYSINTR 的概念、意義及相互關係。
IRQ:
IRQ (Interrupt request),中斷請求。
這裏就是外設或其它請求服務的設備發出來的中斷。屬於硬件中斷,可能是一個電平觸發的
GPIO 中斷,也可能是內部DMA的一箇中斷。
ISR:
ISR (Interrupt service routine), 中斷處理程序。
WinCE實際上使用 ISR來處理中斷,即默認的中斷入口函數,在 ARM體系中,系統默認的
ISR就是 OEMInterruptHandler
IST:
IST (Interrupt service thread), 中斷服務線程。
在 ARM
的結構中,ISR 一般不會用來進行任何實際的操作,而只是返回一個 SYSINTR,
實際的操作全部在IST中完成,IST一般是在Device
Manager 的一個線程中運行的一段
高優先級的應用程序代碼,用來服務實際的中斷請求。
SYSINTR:
在 WinCE中,SYSINTR
就是 system interrupt,就是一個操作系統的邏輯中斷。
一般對於中斷的處理方式都是將一個IRQ映射爲一個或者多個(中斷共享)SYSINTR,而後,
在實際的ISR中根據IRQ返回一個對應的SYSINTR用來告訴操作系統需要服務的邏輯對
象。
使用邏輯中斷的好處當然就是可以實現虛擬的中斷(一個 SYSINTR
就被 OS
認爲是一個獨
立中斷)和中斷共享(單 IRQ對應多
SYSINTR)。
邏輯中斷是WinCE需要處理的實際對象,至於這個對象是一個共享的IRQ,還是一個虛擬
的中斷,還是獨立的物理中斷,系統並不過問,從而隔離了硬件與邏輯,我們的 ISR
需要
做的也正是這種物理中斷到邏輯中斷的映射。
三 WinCE中斷處理原理
下面基於 ARM
體系,來介紹 WinCE
中斷處理的流程與原理。
對於一個硬件IRQ中斷,系統內核在捕獲之後,會交給 OEMInterruptHandler
處理,
這個函數就是我們實現中斷處理的中心函數,首先我們從CPU
的寄存器裏獲得中斷的信息,
這些信息告訴我們到底是哪個 IRQ
源觸發了中斷。
一般實現中斷服務的方式有以下幾種:
1.
簡單中斷處理——ISR模型
最簡單的中斷處理辦法就是在ISR中直接處理,這裏就是指在OEMInterruptHandler
中直接對中斷源進行判斷,然後調用服務程序。
這種方式的優點和缺點一樣明顯。
優點:快速的響應了中斷處理,使系統的實時性能增加不少
缺點:由於進入OEMInterruptHandler的時候關閉了系統中斷(當然你可以在ISR中
自己打開中斷,不過處理起來較麻煩),所以如果處理時間過長,其他中斷很可能被忽略,
造成中斷丟失。並且系統被鎖定,使得系統任務的響應變慢。
2.
中斷處理的一般流程——IST模型
前面看到了 ISR
模型的優缺點。作爲WinCE,主要定位還是民用的消費類電子產品,
所以,對於中斷響應的實時性不是特別高,所以系統的運行響應速度就顯得更加重要。而且
目前的嵌入式設備的處理速度越來越高,已經幾乎達到了當時奔 3
的水平。所以 ISR
的模
型並不適用於WinCE。
如果把中斷服務程序當作一個系統進程或者線程來處理,這樣就不會造成系統被鎖定,
中斷被屏蔽等問題,使得中斷服務程序和其它進程、線程一樣被系統管理調度。於是就有了
IST的概念
IST
模型的想法是,在 ISR
中判斷到具體的中斷源 IRQ,就返回一個標誌,用來標記
需要哪個程序來服務中斷,然後重新打開中斷,恢復系統調度,系統根據這個標誌來激活相
應的程序來服務中斷。
這個就是最常用的中斷處理模型。使得中斷被關閉,系統被鎖定的時間最短。
在 WinCE中,經常使用的就是建立中斷服務線程(IST),然後以IRQ
來申請一個系統
邏輯中斷號(SYSINTR),創建一個事件(Event),將
Event 與 SYSINTR
綁定,隨後 IST
阻塞在等待Event上面。
ISR
中只給系統返回一個 SYSINTR,系統根據綁定關係激活相應的Event,使得隨後
的 IST得以運行。
這就是 IST的一般服務流程
IST模型的缺點就是中斷服務的延遲較大,從 ISR
返回,到 IST開始運行,中間需要
花費一定的時間,相比 ISR
模型的中斷實時性較差,但是這種代價對於我們的應用是值得
的。
四 IST模型的實現
下面我們來看IST模型具體在我們的驅動中是如何實現的。
上面已經介紹了 IST
模型的一般服務流程,下面我們針對驅動程序實例,來分析具體
的實現步驟。
1 驅動程序中 IST的構建與中斷初始化
上面介紹的 IST流程中,很多步驟都是WinCE的內置支持,也就是說你只要調用相應
的 API就可以實現功能了,不需要自己編寫太多的代碼。只需要實現一些流程代碼。
首先是驅動程序端的中斷初始化。假設現在有一個驅動程序,需要服務中斷源,IRQ
爲 0x12。
a)
以 IRQ
爲參數,申請 SYSINTR,方法爲調用
KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &(dwIrq),
sizeof(UINT32), &dwSysIntr,
sizeof(UINT32), NULL)
其中 dwIrq爲IRQ號,即0x12
dwSysIntr 爲系統中斷號,也就是調用返回的結果存放的位置
/* ARMCE深入
實際這個 IOControl調用的是函數 OALIoCtlHalRequestSysIntr,該函數的位置
在
WINCE500\PLATFORM\COMMON\SRC\COMMON\INTR\COMMON\Ioctl.c
最終調用 OALIntrRequestSysIntr,這個由 OEM
自己實現,一般就是維護了 2
張表,一張是從 SYSINTR
到 IRQ
號碼的映射表,另一張是 IRQ
到 SYSINTR 的
映射表,兩者是對應的關係。
這裏注意一點,一個IRQ
映射爲多個SYSINTR
是支持的,也就是共享中斷,但是
系統默認的實現並沒有支持一個 SYSINTR
映射爲多個 IRQ,你可以自己實現。
這兩張表對於後面在OEMInterruptHandler中由IRQ查找SYSINTR提供了方便
*/
b)
創建與 SYSINTR
綁定的Event
由於我們的IST是需要Event激活的,所以這裏申請一個 Event。
申請 Event的步驟比較簡單和標準
hISTEvent = CreateEvent(0,FALSE,FALSE,NULL);
c)
將SYSINTR
與Event綁定
調用 InterruptInitialize(dwSysIntr,hISTEvent,0,0)將 SYSINTR
與 Event
綁
定,用來在OEMInterruptHandler
中返回SYSINTR
時激活相對應的 Event
/* ARMCE深入
InterruptInitialize 的代碼參考 SC_InterruptInitialize
,在
E:\WINCE500\PRIVATE\WINCEOS\COREOS\NK\KERNEL\Intrapi.c中
主要做兩件事情:
#1 將 Event
的 Ptr 填入一個數組,這個數組是記錄每個 SYSINTR
對應激活的
Event
#2
調用OEMInterruptEnable,使能SYSINTR所對應的IRQ,並且將標誌
IRQ
被引用次數的變量加1(WinCE6
的代碼中未見這一變量)
*/
d) 創建一個 IST,並且等待hISTEvent
到了這一步,中斷關於系統方面的初始化基本結束,剩下的就是創建一個 IST,然
後等待 Event來運行中斷服務代碼,例如:
while(TRUE) {
WaitForSingleObject(hISTEvent,INFINITE) ==
WAIT_OBJECT_0)
…
}
這裏需要注意的是IST什麼時候創建都可以,但是在InterruptInitialize之前不要
運行 IST
以等待這個 Event,也就是說在 InterruptInitialize
之前不要使用這個
Event,否則會導致InterruptInitialize失敗。
還有就是不要使用WaitForMultipleObjects來等待Event。
在中斷處理完成之後需要調用 InterruptDone,參數爲該中斷的SYSINTR。來通
知系統,中斷處理完成,系統重新使能該中斷
到了這裏,驅動的中斷初始化工作就全部完成了。
2 OEM層需要做的工作
OEM 層主要是控制 IRQ
的 enable (BSPIntrEnableIrq)
與 disable
(BSPIntrDisableIrq), 當然要初始化 IRQ
的配置,使其在正確的觸發狀態,比如上升延
觸發
至此一箇中斷處理的IST模型就實現了,系統在IRQ
觸發時調用映射函數,獲得相應
IRQ 的 SYSINTR,然後返回合法的SYSINTR給系統,系統查表激活相應的Event,對應
的 IST進行中斷服務,然後再次等待 Event。
3
中斷資源的釋放
當不需要當前中斷繼續服務的時候可以通過調用 KernelIoControl
來釋放申請到的
SYSINTR,具體格式爲:
KernelIoControl(IOCTL_HAL_RELEASE_SYSINTR, dwSysIntr, sizeof(DWORD),
NULL, 0, NULL);
其中 dwSysIntr 就是需要釋放的 SYSINTR 號碼。
/* ARMCE深入
這裏實際調用的是OALIntrReleaseSysIntr這個函數,由OEM自己實現。所做的動作也
很簡單,就是從SYSINTR
到 IRQ
和 IRQ 到 SYSINTR
的那兩個映射關係數組中刪除映射
關係。
*/
五可安裝的 ISR
1 爲什麼要使用可安裝 ISR(以下簡稱
IISR)
需要 IISR
的目的有兩種:
I. 動態的安裝中斷程序
在系統運行的過程中註冊中斷,這種中斷一般是不可預知設備的中斷,常用在總線設備
中,比如PCI設備
II.
中斷共享
當多箇中斷源使用同一個中斷號(IRQ)時,就需要使用 IISR
來實現了
當然如果是需要動態安裝的共享中斷就最適合了。
因爲我們的 IST模型中,中斷服務程序就是驅動中的IST,IRQ與
IST是一對一的關
系。所以在需要動態添加一箇中斷服務程序的時候就沒有辦法處理了。
同樣由於 IRQ
與 IST 的一一對應關係對於一個 IRQ
對應多個需要服務的 IST
就同樣
沒有辦法處理。
基於上面的情況纔會有IISR
的出現,IISR
從本質上是在ISR 中提供了一個接口,當
ISR 調用 NKCallIntChain時,以此IRQ爲參數,在鏈表中依次查找是哪一個服務程序來
服務這次 IRQ,然後返回相應的 SYSINTR,此後的動作與
IST 模型就基本一樣,通過
SYSINTR 來激活Event,從而啓動相應的 IST。
所以 IISR
的實現就是動態的向某一個 IRQ服務程序鏈表添加結點的過程。
2 IISR的實現
下面我們來看看IISR
的具體實現步驟:
首先我們需要了解IISR
服務中斷的實現原理,如上面描述的,根據IRQ,來順序調用
鏈表中的中斷處理程序。所以我們可以有兩個選擇,一個就是類似 ISR
模型,直接在鏈表
中的中斷處理程序中判斷是不是自己的中斷,並且做處理。還有一種方式就是類似 IST
模
型,如果判斷是自己的中斷,則返回一個SYSINTR,以此SYSINTR
來激活IST。
無論哪種方法,關於註冊中斷和查詢中斷的方式是一樣的,下面我們來看下如何將中斷
程序添加到鏈表,又如何在中斷來的時候去搜索鏈表。
Microsoft提供了一個通用的IISR的處理模型,叫做 GIISR,這是一個以 IST模型處
理 IISR
的模塊,源程序可以在WINCE500\PUBLIC\COMMON\OAK\DRIVERS\GIISR
找到。熟悉了 GIISR,想實現自己的 IISR
處理程序或者基於 ISR
模型的處理,都比較簡
單了。
下面我們就分析這種比較通用的處理 IISR
的模型。
a) 首先我們需要以 IRQ
來申請 SYSINTR,並且將 SYSINTR
與 Event
綁定,這些
步驟與IST模型中介紹的一樣,這裏就不重複敘述了,IISR
在這裏與 IST模型並
沒有任何的不同。其與 IST
模型的唯一不同點就是如何根據 IRQ
來判斷相應的
SYSINTR。
在 IST
模型中是 OEM寫死的一個判斷程序,而 IISR
可以動態來註冊一個判斷程
序給系統調用,這是唯一的實現區別。
b) 下面我們需要註冊可安裝中斷程序的 dll,和 dll中的中斷處理函數,並且將他們與
某一個特定的IRQ相關聯
這個過程是通過調用LoadIntChainHandler函數來實現的。
這裏我們的中斷服務dll叫做”giisr.dll”,處理函數名叫做”ISRHandler”,對應IRQ
爲0x20,則函數調用形式如下:
HANDLE hIsrHandler = LoadIntChainHandler(TEXT(“giisr.dll”),
TEXT(“ISRHandler”), 0x20);
/* ARMCE深入
LoadIntChainHandler的源代碼可以在WINCE500\PRIVATE\WINCEOS\
COREOS\NK\KERNEL\Loader.c中找到,就是函數SC_LoadIntChainHandler
WinCE6 在 KDriver.c
中的 NKLoadIntChainHandler,兩者功能大致相同,只
是在一些結構體定義上略有不同
其主要功能就是加載 dll
到 NK,並且獲得三個函數的指針,一個是
CreateInstance,一個是你剛纔傳進來的處理函數,這裏就是 ISRHandler,還
一個就是 IOControl,後面會用到。
首先調用CreateInstance
來獲得一個實例的數據,這個數據就是一個 index,用
來標示其中的一箇中斷處理程序的索引。
這裏可能需要解釋下 GIISR
的處理原理。我們所有的可安裝中斷都通過 giisr.dll
裏面的 ISRHandler來處理,在NKCallIntChain被調用的時候,會遍歷所有註冊
到這個 IRQ
的中斷處理函數,這裏全部都是同一個函數 ISRHandler。那麼就需
要可以區分每一次調用,所以就在 giisr
模塊裏面維護一個數組,每一箇中斷服務
程序佔用一個數組成員,這些數組成員的Index就是他們在giisr裏面的唯一標識。
所以 CreateInstance
的任務就是查找數組,找到第一個空閒位置,將 Index
返
回。在ISRHandler
被調用到的時候,會將這個Index傳遞進去,根據這個 Index,
ISRHandler 就能夠知道是數組中哪個成員正在被查詢,進而確定是不是這個成員
需要處理中斷請求,進而確定該返回的 SYSINTR。詳細的步驟下面會一一說明,
大家先有一個概念
在從 CreateInstance
返回了可用的數組 Index
之後,調用 HookIntChain,此
函數在Kdriver.c中。這個函數的功能比較簡單,我們先了解下共享中斷在系統中
的處理。
前面有所提到,在調用 NKCallIntChain
時會遍歷一個鏈表,每個鏈表頭就是系統
維護的一個數組中的一個成員,每一個 IRQ
號碼都對應數組中的一個成員,這個
數組就是 pIntChainTable。IRQ
按照其號碼在數組中對應相應的數組成員,注意
pIntChainTable 是一個 256
個成員的數組,這也就意味着你的 IRQ
號碼數字不
能超過 255,當然這是指你傳遞進來的 IRQ
號碼,如果你的 IRQ
號碼都是大於
255 的,可以對實際 IRQ
號碼做處理,保證其數字值在 0-255,比如對 256
取模,
這樣當然就可以傳遞進來了。
pIntChainTable中的每個元素都是一個鏈表的頭,當你向一個IRQ
添加中斷處理
的時候,實際是建立了一個新的PINTCHAIN元素,然後向pIntChainTable中的
IRQ 索引位置去添加,如果該位置不爲空,則查找這個元素指向的下一個元素,在
這個單向鏈表的操作下,將新的中斷處理程序加入。
這個加入工作就是HookIntChain做的
*/
c)
上一步在GIISR中通過CreateInstance把這個新的中斷處理程序加入GIISR自
己的管理。GIISR
的主要作用就是判斷當中斷來的時候,是不是其內部數組中的某
個成員需要服務中斷。所以需要更多的信息用來判斷中斷是否匹配當前的中斷服務
程序,所以我們需要把信息傳遞進去,這裏就是調用KernelLibIoControl。
具體的方法爲:
KernelLibIoControl(hIsrHandler,IOCTL_GIISR_INFO,&giisr_info,
sizeof(GIISR_INFO), NULL, 0, NULL);
這裏就是把 giisr_info
的內容傳遞給剛纔註冊的中斷,giisr_info
是一個
GIISR_INFO的結構體,其內容如下:
typedef struct _GIISR_INFO {
DWORD SysIntr; // SYSINTR for ISR handler to return
(if associated device is asserting IRQ)
BOOL CheckPort; // If true, check port to see if device is
asserting IRQ
BOOL PortIsIO; // Port is IO port (possibly true only for
x86)
BOOL UseMaskReg; // If true, read from MaskAddr to
obtain mask
DWORD PortAddr; // Port Address
DWORD PortSize; // Port data width in bytes
DWORD Mask; // Mask to use on data port to
determine if device is asserting IRQ
DWORD MaskAddr; // Address of register to use as
mask
} GIISR_INFO, *PGIISR_INFO;
這些成員都是需要設置的,具體含義如下
SysIntr:這個中斷所對應的系統中斷號,即第一步申請到的 SYSINTR,系統在
確定是當前的設備出發的 IRQ
之後會返回這個 SysIntr
CheckPort:一般爲 TRUE,如果爲 FALSE
則直接返回 SysIntr,而不是判斷是
不是當前設備觸發的中斷
PortIsIO:是不是IO端口,這個可能只是在 x86
下使用,我們置爲 FALSE
UseMaskReg:是否使用地址來獲得 Mask,如果爲TRUE,則Mask
字段無意義
PortAddr:實際上是可以判斷中斷是哪個設備出發的那個寄存器的地址
PortSize:PortAddr的位寬,標誌PortAddr是1字節(BYTE),2
字節(WORD),
還是 4字節(DWORD)的寄存器,其他不支持
Mask:一個掩碼位,在UseMaskReg爲FALSE
的情況下與PortAddr的值進行
位或運算,如果不爲0,則確定爲當前設備觸發的中斷
MaskAddr:當 UseMaskReg爲 TRUE
的時候,使用這個地址來獲得掩碼的值,
給動態的判斷中斷提供了接口
仔細看了上面各個成員的介紹,大家就應該可以瞭解 GIISR
是如何判斷中斷是不
是當前設備產生的。
所有的判斷依據就是這個結構體。一般我們會將 CheckPort
置爲 TRUE,然後讓
系統去讀取PortAddr地址處的值,這個值可以標誌是否爲當前設備觸發的中斷。
獲得這個值以後,與一個 mask
值進行或運算(&),如果值不爲 0,則認爲是當前
設備觸發的中斷。這個 mask
值在 UseMaskReg
爲 FALSE
時是成員 Mask,反
之是從 MaskAddr
地址處獲得。
/* ARMCE深入
KernelLibIoControl 的源代碼可以在
Kdriver.c 中找到,就是
SC_KernelLibIoControl 。 SC_KernelLibIoControl
直接調用的
NKKernelLibIoControl,這個函數同樣在 kdriver.c
中。分析源碼可知,就是調
用了 giisr.dll
中的 IOControl,參數爲 IOCTL_GIISR_INFO,就是傳遞進去了
一個 GIISR_INFO
結構體,用來給 Index
標示的數組元素賦值,這個 Index
就
是前面CreateInstance返回的那個數值,通過 hIsrHandler傳遞進去。
*/
d) 下面就是啓動IST,等待Event,這裏和IST模型沒有任何區別。
到這裏全部的初始化就完成了,可以看出,和IST模型相比就是多了兩步 b)和
c),
這兩步決定了中斷判斷的方式,這也是 IISR的根本所在。
3 中斷的判斷
下面詳細介紹下可安裝中斷在 ISR
中被判斷的過程。
同樣,中斷到來時進入的函數是
OEMInterruptHandler ,在
OEMInterruptHandler中會調用NKCallIntChain來遍歷該IRQ對應註冊的IISR。
這是一個鏈表結構,所以對中斷判斷程序是一個順序調用的過程,即先註冊的設備先
判斷,如果判斷到正確的結果,則返回合法的 SYSINTR,OEMInterruptHandler
也同樣返回這個值。所以即使後面的設備也符合條件,也不會被執行。如果整個鏈表
中都沒找到正確的設備,則返回 SYSINTR_CHAIN。OEMInterruptHandler
在判
斷到返回結果爲SYSINTR_CHAIN
時,即表示請求中斷的設備不在鏈表中。
/* ARMCE深入
NKCallIntChain 的代碼在 KDriver.c
中,是個非常簡單的函數,以傳遞進來的
IRQ 爲 Index
找到鏈表的頭,然後去調用各鏈表節點的 hIsrHandler,並且以
CreateInstance 的返回值爲參數。當有返回的值不是 SYSINTR_CHAIN
時,就返
回這個值,反之繼續查找,直到鏈表尾部,如果還沒有找到,就返回
SYSINTR_CHAIN。
*/
4
自定義的 IISR
我們可以不使用GIISR,而自己實現IISR功能,只要知道了IISR
的原理。
當以某一個寄存器或者地址的值,不足以判斷到底是系統中哪個設備觸發的中斷
的時候,GIISR
就不是這麼好用了。比如多個不同的外設,使用同一個GPIO
來觸發
中斷。外設需要讀取多個寄存器或者需要一個複雜些的計算(不只是簡單的一個&操作)
才能判定中斷是否是其產生的。這時候我們需要使用自己的一套 IISR的處理方式。
當然我們不希望去改動整個微軟對於 IISR
的處理結構,所以我們就要區分開來上
面介紹的 GIISR
的模型裏,哪些是微軟的架構,哪些是GIISR
自己的實現。
微軟的 IISR
架構:
a) 首先需要使用 LoadIntChainHandler
去註冊這個 IISR
的處理判斷程序的
dll,在這個 dll中除了需要一個判斷處理程序(也就是通過
LoadIntChainHandler傳遞進去的那個參數),還需要一個CreateInstance
的函數,這個是必須的。在你不改動微軟內核的情況下,名字也是固定的,詳
細地函數定義,可以參考 GIISR
的 CreateInstance。至於 IOControl,最
好也參考 GIISR
的定義一個,如果不需要去調用KernelLibIoControl
的話,
應該是可以不實現的。
b)
在OEMInterruptHandler
中調用NKCallIntChain去遍歷鏈表,在調用處理
函數時將CreateInstance
的返回值作爲參數傳遞進去,如果處理函數在判斷
到不是自己觸發的中斷,應該返回 SYSINTR_CHAIN,否則返回一個有意義
的 SYSINTR
值
c)
在需要註銷這個IISR
的時候,調用 FreeIntChainHandler,需要在dll中實
現 DestroyInstance
這個函數,被系統調用,這個是可選的。
GIISR
自己的實現:
使用同一套代碼管理這些中斷處理程序,每個中斷處理程序在內部的數組中佔用
一項,這些項目記錄着中斷處理程序激活使用的SYSINTR
以及判定其觸發中斷的標
準。這個標準就是讀取某個寄存器或地址來用掩碼來判斷。同時這個數組項目對應的
結構體數據就是通過KernelLibIoControl
傳遞進去的。
所以對於需要使用自己的特殊方式判斷中斷觸發的程序,可以使用自己的中斷判別
程序。
我們可以爲每一箇中斷外設都實現自己的處理dll。在調用LoadIntChainHandler
時傳遞進自定義的一個 dll
與處理函數,CreateInstance
一定要實現,不過返回值
可以忽略。
在處理函數中,我們直接根據自己的外設來判斷中斷條件,然後返回相應的
SYSINTR。
其實使用 CreateInstance
的含義就是想把同一判斷類型的設備使用一套統一的
處理函數來判斷。CreateInstance
的返回值就是區別不同設備的這個 Index。
5 IISR資源的釋放
當我們需要註銷這個 IISR
的時候,需要調用 FreeIntChainHandler,來將該中
斷服務從鏈表中刪除。
FreeIntChainHandler(hIsrHandler);
hIsrHandler 就是LoadIntChainHandler
的返回值。
/*ARMCE 深入
FreeIntChainHandler 函數實現同樣在 loader.c
中的SC_FreeIntChainHandler。
所做的動作就是,如果有 DestroyInstance
函數,則調用 DestroyInstance。然後
調用 UnhookIntChain。UnhookIntChain的函數實現在
kdriver.c 中,是一個標準
的單向鏈表刪除其中一個元素的操作,這裏不再詳細解釋。
*/
6
使用 IISR的注意事項
由於 IISR
是動態的被加載的,也就是說註冊的 dll
會被加載到內核空間。所以不
能調用任何非內核的函數。
並且,如果將 IISR的 dll放在 bib文件的MODULES
section裏面,需要設置”K”
屬性,如
giisr.dll $(_FLATRELEASEDIR)\giisr.dll NK SHK
如果放在 FILES section
裏面,需要保證沒有 fixup類型的變量。
六 WinCE
中斷的延遲
1 造成中斷延遲的原因
圖 1 WinCE 中斷體系結構
重新看圖 1。這裏畫出了從硬件中斷髮生,到 IST
運行的全部過程,中間就是
我們要研究的延時部分。
圖中的”IST
延遲”標誌,實際上就是我們所說的中斷延時。但是將 IST
延時細
分,可以分爲如下幾部分延時:
a) ISR 延時
這部分延時是指從硬件觸發中斷到進入OEM的 ISR程序的時間,在 ARM
體系
下,就是進入OEMInterruptHandler
的時間。
b) ISR
執行時間
在 OEMInterruptHandler
中判斷 IRQ,並且返回相應的 SYSINTR
的時間,
也是OEM最主要把握的時間。
c) 從ISR
返回到 IST運行的時間
主要就是系統根據ISR
返回的SYSINTR激活相應的Event,系統調度運行,
到 IST執行的這段時間
2
如何降低中斷延時
上面的中斷延時原因中,a)是我們一般不去過問的部分,這段代碼是微軟實現
的,原則上是可以改的,但是收效不大,而且可能引入 bug,建議不要去動。c)是
我們可以部分控制的,關鍵就是IST的優先級,其他部分我們也使用微軟默認的實
現。
b) 這一部分就是我們着重需要管理的了,因爲這一塊就是
OEMInterruptHandler 函數的實現,是 OEM
自己實現的代碼。也就是說,判斷
中斷源,返回SYSINTR的過程是我們唯一較可行的控制中斷延時的地方。
關於這部分的優化,首先就是儘量用較簡單的邏輯判斷 IRQ,來返回相應的
SYSINTR,而且 OEMInterruptHandler
會調用NKCallIntChain,所以 IISR
的
鏈表長度與 IISR處理函數的效率也是影響中斷延時的重要因素。
一般在判斷 IRQ
的過程中,我們會把最可能出現、最頻繁出現的IRQ
放在最前
面判斷,比如系統時鐘。這樣在剛進入OEMInterruptHandler就可以判斷到IRQ,
並返回,節省了時間,提高了效率。同樣這種方法也適用於 IISR,將最可能出現,
最頻繁出現的設備放在鏈表的前面,可以提高遍歷的效率。
七總結
WinCE
下提供了較靈活的中斷處理方式,包括ISR,IST,IISR
三種主要方式。
對於 ARM
架構下的系統開發,我們常用的就是 IST
與 IISR
兩種方式。兩種方式
從本質上都是通過返回正確的SYSINTR來激活相應的 IST來服務中斷。不同點只是對
IRQ 判斷的方式,IST
是內置的 OEM
寫死的判斷程序,而 IISR
是通過一個預留的接
口,來動態註冊判斷程序,從而給了系統一個註冊新中斷的機會。同時 IISR
可以實現
中斷共享,在一個 IRQ
上通過鏈表的方式不斷添加判斷程序,從而讓多個設備共享同
一個 IRQ,同時又可以有自己獨立的中斷判斷程序。
在最後,我們分析了中斷延時的一些因素,這裏並沒有詳細的進行分析,只是提出
了一些降低中斷延時的方法。
參考文獻:
1. Microsoft Windows CE .NET 中的中斷體系結構 , Nat Frampton
2. Platform Builder for Microsoft Windows CE 5.0 Help, Microsoft
3. Microsoft Windows Embedded CE 5.0 source code, Microsoft