Chinaunix首页 | 论坛 | 博客
  • 博客访问: 443724
  • 博文数量: 50
  • 博客积分: 2635
  • 博客等级: 少校
  • 技术积分: 760
  • 用 户 组: 普通用户
  • 注册时间: 2008-03-16 14:02
文章分类

全部博文(50)

文章存档

2013年(3)

2012年(18)

2011年(7)

2009年(1)

2008年(21)

我的朋友

分类: WINDOWS

2008-07-25 18:29:24

看见论坛上有个朋友对WinCE上的Interrupt Handling不太了解,何牛又回帖说太忙。

Interrupt Handing,先要从Interrupt Installation谈起。
(以x86平台为例,arm我不会,sorry~)
在Kernel startup的过程中,会首先调用OEMInit(), 做OAL上的初始化,而这其中关于interrupt的部分又包括
OALIntrInit()       // 将PIC上的物理IRQ与系统定义的SYSINTR_XXX相关联
x86InitPICs()       // 调用HookInterrupt()

然后,interrupt就enable了。目前为止,还只是完成了Windows CE 3.0上的Interrupt Installation,在目前的CE上,还支持动态的加载中断(Installable ISR, IISR, 我不知道怎么翻译好)以来支持不同的ISR共享同一根中断信号线。包含IISR的DLL通过调用kernel当中的 LoadIntChainHandler()来实现动态的加载中断, 而LoadIntChainHandler()最终会调用到
SC_LoadIntChainHandler()(PRIVATE\WINCEOS\COREOS\NK\KERNEL\Loader.c).

在SC_LoadIntChainHandler(
LPCWSTR lpszFileName,           // 模块名
LPCWSTR lpszFunctionName,       // ISR
BYTE bIRQ
),

每个IISR都需要提供CreateInstance()这样一个函数,因为在SC_LoadIntChainHandler()中,会调用 CreateInstance()以来得到ISR所需要的参数。然后通过HookIntChain()将ISR挂到对应的Irq的链表上
而HookIntChain() (PRIVATE\WINCEOS\COREOS\NK\KERNEL\kdriver.c)
其实也是个很简单的函数,Kernel维护着一张INTCHAIN (PRIVATE\WINCEOS\COREOS\NK\KERNEL\kdriver.c)的表,以IRQ为索引,相同的IRQ又用链表的形式组织起 来,这样就把整个Kernel当中的IRQ和IISR组织了起来。

OK, 上面就是中断注册的全部过程

当一个中断发生时,OAL ISR会根据IRQ得到对应的SYSINTR,具体的做法也很简单,就是调用NkCallIntChain()遍历上面提到的链表,如果有设备注册了该 IRQ号(或者说,该设备为此中断的中断源)那么就到用对应的IISR(Installable ISR)。注意,因为是顺序遍历,所以早注册的IISR会首先得到处理。如果在所有注册的IISR上都不处理(NkCallIntChain()返回为 SYSINTR_CHAIN),那么OAL上的ISR会通过OALIntrTranslateIrq()将IRQ通过在OALIntrIrq()建立的静 态对应关系,来得到对应的SYSINTR。
OAL ISR在最后会关闭所有较低或者相等的IRQ号,并且向PIC中写入EOI表示ISR的处理结束


在HookInterrupt()(PRIVATE\WINCEOS\COREOS\NK\KERNEL\x86\sysinit.c)中,有这样的一段代码
if (pStub->pfnHandler == NULL) {
   pStub->PushEax   = 0x50;
   pStub->Pushad     = 0x60;
   pStub->MovEsi    = 0xBE;
   pStub->pfnHandler = pfnHandler;
   pStub->JmpNear   = 0xE9;
   pStub->JmpOffset = ((DWORD)&CommonIntDispatch) - ((DWORD)(&pStub->JmpOffset) + 4);

   InitIDTEntry(hwInterruptNumber, KGDT_R0_CODE, pStub, INTERRUPT_GATE);

   return(TRUE);
}
无疑的,这是一段初始化IDT条目的代码,上面的一堆赋值语句,初看觉得很奇怪,但是继续发掘CommonIntDispatch()下的代码,可以发现
call            esi
mov    ecx, dword ptr [pfnOEMIntrOccurs]      // (ecx) = pfnOEMIntrOccurs
push    eax                 // push argument == SYSINTR returned
call     ecx                // call pfnOEMintrOccurs
pop     ecx                 // dummy pop

如果对汇编足够熟(熟到知道Pushad的机器码是60这样的程度)或者加上一点想象的话,那就不难发现,其实那段代码就是一段x86汇编的机器码
PUSH EAX;
PUSHAD
MOV ESI pfnHandler
JMP near offset &CommonIntDispatch          //这里还有一个偏移没有计算。

但最后IDT中保存的entry就是这段machine code,也就是,换言之,如果执行中断的话,那就会执行这段代码从而调用commonIntDispatch()然后再从CommonIntDispatch中调用对应的OAL ISR得到相应的SYSINTR.

注意Kernel中的KDataStruct(private\winceos\coreos\nk\inc\nkx86.h)这个数据结构
其中的,LPEVENT alpeIntrEvents[SYSINTR_MAX_DEVICES];
就是将SYSINTR与EVENT关联起来的数组。

继续回到CommonIntDispatch()中来,就会发现kernel是靠一次RunThread()来唤醒IST的。挨下来的工作就是与线程的调度相关的了……
阅读(2627) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

jxggj2014-01-26 10:30:33

老师你好,我现在在学习win下的中断,平台是X86的。因为网上的资料大多数是ARM的,所以我遇到了很多问题,我想请教您。
X86没有oeminterrupthandler,这个函数,是不是意味着一定要使用可安装ISR来处理自己的中断尼?请老师抽空解答一下我的问题,我看很多资料也没找到头绪。谢谢!