NDISWDM代码学习笔记.docx
- 文档编号:20686342
- 上传时间:2023-04-25
- 格式:DOCX
- 页数:13
- 大小:621.54KB
NDISWDM代码学习笔记.docx
《NDISWDM代码学习笔记.docx》由会员分享,可在线阅读,更多相关《NDISWDM代码学习笔记.docx(13页珍藏版)》请在冰豆网上搜索。
NDISWDM代码学习笔记
NDISWDM代码学习笔记
NDISWDM是微软ddk中提供的示例,这里基于ndis5.0例子进行的笔记。
该驱动属于miniport驱动,并与NDISPROT驱动进行通信。
NDISPROT是ddk中的协议驱动。
两个驱动看下来,感觉NDIS的驱动确实有点麻烦,一方面与其他驱动结构上差别比较大,包括api方面,另一方面要时刻注意硬件的状态。
下面是初步的笔记。
如果你想快速阅读,可以直接略过第一部分Adapter的状态,个人觉得第二部分IRP的Cancel和第三部分对象的生命周期还是有点样子的。
该笔记中并未介绍NDIS驱动的结构,假设了你已经了解NDIS驱动的基本知识。
并请在阅读的之前,能先通读一下NDISWDM的代码。
保持代码随手可翻的状态。
下面开始吧。
一、Adapter的状态
Adapter所涉及的Flag如下
#definefMP_RESET_IN_PROGRESS0x00000001
#definefMP_DISCONNECTED0x00000002
#definefMP_HALT_IN_PROGRESS0x00000004
#definefMP_SURPRISE_REMOVED0x00000008
#definefMP_RECV_LOOKASIDE0x00000010
#definefMP_INIT_IN_PROGRESS0x00000020
#definefMP_SEND_SIDE_RESOURCE_ALLOCATED0x00000040
#definefMP_RECV_SIDE_RESOURCE_ALLOCATED0x00000080
#definefMP_POST_READS0x00000100
#definefMP_POST_WRITES0x00000200
从下往上看,研究各个Flag的作用。
1.fMP_POST_READS和fMP_POST_WRITES
fMP_POST_READS在NICInitAdapterWorker中设置,设置好后,在NICFreeRCB中检测当前的Read是否小于某个阀值,如果小于,检测下fMP_POST_READS,如果可以,则调度ReadWorkItem执行。
。
而在NICReadRequestCompletion中,检测到某个IRP失败了,就清空fMP_POST_READS。
当然,在MPHalt中也清空了该Flag。
于是,我相信,你懂的,该Flag就是用来表示当前的Adapter能不能投递Read的Request
fMP_POST_WRITES在NICInitAdapterWorker中设置,设置好后,在MPSendPackets中检查,如果可以,则投递该Write请求。
此处呢,则是在MPHalt中清空。
当然你也可以在Write失败时清空。
于是,同样的,你懂的,该Flag就是用来表示当前的Adapter能不能投递Write的Request
2.
fMP_RECV_SIDE_RESOURCE_ALLOCATED和fMP_SEND_SIDE_RESOURCE_ALLOCATED
fMP_RECV_SIDE_RESOURCE_ALLOCATED在NICAllocRecvResources中设置,设置好后,在NICFreeRecvResources和NICFreeBusyRecvPackets中检查。
如果该Flag设置了,则执行后面的释放操作。
释放完,清空该Flag。
所以,不解释。
fMP_SEND_SIDE_RESOURCE_ALLOCATED在NICAllocSendResources中设置,设置好后,在NICFreeSendResources中检查。
有设置,则执行释放操作,并清空该Flag。
同时在NICFreeQueueSendPackets和NICFreeBusySendPackets所以同样的不解释。
3.
fMP_INIT_IN_PROGRESSfMP_HALT_IN_PROGRESS和fMP_RESET_IN_PROGRESS
fMP_INIT_IN_PROGRESS在MPInitialize中设置,然后在NICInitAdapterWorker中检查该状态。
如果设置了,则继续运行。
在NICInitAdapterWorker末尾处,该Flag被清空。
也就是说,该Flag标记Adapter当前的初始化状态。
fMP_HALT_IN_PROGRESS只有MPHalt中设置了。
表示已经Halt了。
fMP_RESET_IN_PROGRESS直接没找到设置的地方。
4.fMP_DISCONNECTED和fMP_SURPRISE_REMOVED
fMP_DISCONNECTED在NICInitializeAdapter中设置,当然在NDIS_STATUS_MEDIA_DISCONNECTED时也会设置该Flag。
当然会在NDIS_STATUS_CONNECTED时清空。
在NICGetMediaConnectStatus时被检查。
fMP_SURPRISE_REMOVED在MPPnpEventNotify中被设置,在MPHalt中检查如果它没被设置,则表示没被拔出,调用MPShutdown释放相应的资源
当然
(fMP_DISCONNECTED|fMP_RESET_IN_PROGRESS|fMP_HALT_IN_PROGRESS|fMP_INIT_IN_PROGRESS|fMP_SURPRISE_REMOVED)
这几个Flag作为Adapter是否准备好的判断条件,分别被用在
NICPostReadsWorkItemCallBack向Adapter投递Read的Irp。
MPQueryInformation向Adapter投递查询的Request
MPSetInformation向Adapter投递设置的Request
MPSendPackets向Adapter投递Write的Irp
也就是说Adapter是否准备好,将决定着我们能否向Adapter投递各种Request
2、IRP的Cancel
在经常使用异步处理的驱动当中,IRP的取消变得非常重要。
下面参照NdisWdm进行学习。
IRP的Cancel目的就是让该IRP后面的处理不再进行。
主要分成以下几种情况:
1.Irp没进行完这个时候直接IoCancelIrp取消该Irp的执行,无需后续处理
2.Irp已经执行完了,只是还没执行到CompleteRoutine,这个时候取消没有效果,
该怎样的已经怎样了。
等到进入了CompleteRoutine,系统意识到你想取消该Irp
就把该Irp重新交给你了,系统不会继续向上让它完成
3,该Irp事实上已经完成了,而且完成函数已经开始调用了,只是该IRP可能还未释
放。
这个时候我们不调用IoCancelIrp取消它。
我可以肯定的讲,Irp的Cancel在Ndiswdm中已经成为一种固定的思路,你可以把这个当做驱动开发中的模式。
一开始,在要调用IoCallDriver发送该Irp之前,将IrpLock设置为IRPLOCK_CANCELABLE,表示该Irp是可以被Cancel的,之后调用IoCallDriver。
然后呢?
调用IoCallDriver之后可能有两个情况:
一个是该Irp完成了;一个是要取消该Irp。
显然,我们不能随随便便就取消一个Irp了,一个已经完全完成的Irp我们没办法Cancel。
那咋办?
也就是说,我们总得根据情况CancelIrp。
对于一个已经完成的Irp,是不能Cancel的。
只有正在进行的Irp我们才能Cancel。
于是,我们要给Irp标记状态。
一个已经完成的Irp,状态标记为IRPLOCK_COMPLETED,此时该IRP不能取消。
于是我们在完成函数里面,第一件事就是将该Irp的状态设置成IRPLOCK_COMPLETED。
一个正在进行的Irp,可以取消,标记为IRPLOCK_CANCELABLE。
于是我们在IoCallDriver之前,先把该Irp设置成IRPLOCK_CANCELABLE。
在取消之前,我们先检查状态,如果该Irp已经被标记为IRPLOCK_COMPLETED,我们直接放弃取消。
否则,我们就取消。
但是,聪明的你一定发现还有另外一种临界情况:
我们对一个标记为可Cancel的Irp进行
IoCancelIrp时,它已经进入完成函数了。
这个时候呢?
我们显然希望它的那些在完成函数中的后续工作能被我们Cancel掉。
于是我们在IoCancelIrp之前先给该Irp做个标记,表示,因为该Irp已经开始被Cancel了,如果之后该Irp没有被成功地Cancel掉,继续走到了完成函数里(显然我们确定它可以Cancel的时候,它还没到完成函数里),那么请它放弃执行后续操作,把自己交给我们,由我们统统处置。
于是我们在完成函数里不仅要设置该IRP的状态为IRPLOCK_COMPLETED,还要检查该IRP到底是不是已经开始Cancel了。
如果是,放弃后续处理,主动交出自己。
以下就是操作模板:
1.在IoCallDriver之前,设置该Irp是可以Cancel的。
代码参见NdisWdm的Receive.c函数NICPostReadRequest
2.在完成函数中设置IRP的状态,并检查IRP先前的状态
代码参见NdiswdmReceive.c函数NICReadRequestCompletion
3.在取消IRP的函数中,设置IRP的状态,并检查先前状态。
在IoCancelIrp之后再次检查状态,看看有没有漏网之鱼
代码参见NdisWdmReceive.c函数NICFreeBusyRecvPackets
3、对象的生命周期
当然Adapter刚产生时,它的RefCount自动被设置成1。
表示该Adapter产生了。
每次要Shedual一个workitem的时候,都会调用MP_INC_REF(Adapter)。
为的就是在MPHalt中等待所有异步的workitem完成,才释放所有资源并退出。
参见Init.cNICInitAdapterWorker
当该WorkItem完成时,执行MP_DEC_REF(Adapter)
参见Receive.cNICPostReadsWorkItemCallback
在MP_DEC_REF中干啥了?
参见Ndiswdm.h
也就是说只有Adapter的RefCount为零时,才会设置HaltEvent。
那谁会等待该Event?
参见Ndiswdm.cMPHalt函数
MPHalt为啥要等待该Event?
当然是为了等各个还在运行的WorkItem结束。
参见MPHalt的说明。
Ndiswdm.cMPHalt函数
如果你要具体研究,可参看另一个workitem,NICRequestWorkItemCallback或者StatusIndicationIrp。
问题是为何异步的StatusIndicationIrp使用MP_INC_REF这种方法而异步的TCB和RCB就不使用INC_MP_REF呢?
因为StatusIndicationIrp中使用了该Adapter,发送的Irp的SystemBuffer就是Adapter的NdisProtIndicateStatus。
如果此时Adapter的资源被释放掉了,显然处理该Irp的驱动就会崩溃掉。
说到底,该计数是用来管理对象的生命周期的。
如果该对象中的内容正在被使用,则该对象计数增加1;完成使用,递减1。
直到该对象计数为0,该对象可以被释放。
那两个workItem就更是如此了。
要使用该Adapter进行SendRequest。
参见Request.c
再看看TCB和RCB的管理。
TCB和RCB都是额外分配的空间,并未使用Adapter中的空间。
在TCB要被使用时初始化Ref为1.
参见Send.c函数NICCopyPacket
在完成该Send时被递减,发现引用为0时,释放资源。
参见Send.c函数NICWriteRequestCompletion
当然,如果该Irp刚完成被Cancel了,由那个Cancel的家伙负责释放资源。
参见Send.c函数NICFreeSendPackets
当然,如果你是取消了StatusIndicationIrp这种的,由于所使用到的资源是Adapter,你要做的就是递减该Adapter的计数。
参见Init.c函数NICCancelStatusIndicationIrp
再次查看MPHalt,你就会明白,驱动的作者在下一盘很大的棋,目的就是为了MPHalt能正常结束。
MPHalt中也就是把那些能Cancel的Irp都Cancel了,不能Cancel的通过对象管理计数,在完成函数或者Cancel的处理函数中正确释放该资源。
1.释放能释放的资源(这边能Cancel的都Cancel了)
参见Ndiswdm.cMPHalt函数
2.等待尚未被释放的资源
参见Ndiswdm.cMPHalt函数
3.完整释放所有资源
参见Ndiswdm.cMPHalt函数
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- NDISWDM 代码 学习 笔记
![提示](https://static.bdocx.com/images/bang_tan.gif)