WDM驱动程序设计_第1页
WDM驱动程序设计_第2页
WDM驱动程序设计_第3页
WDM驱动程序设计_第4页
WDM驱动程序设计_第5页
已阅读5页,还剩35页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、WDM驱动程序设计同步技术第 5 讲主要内容 一个同步问题的例子 中断请求级 自旋锁 内核同步对象 其它内核同步原语一个同步问题的例子下面利用静态变量lActiveRequests记录当前未完成的I/O请求数: static LONG lActiveRequests;NTSTATUS DispatchPnp(PDEVICE_OBJECT fdo, PIRP Irp) +lActiveRequests; . / process PNP request -lActiveRequests;有什么问题?关于语句“+lActiveRequests”在X86处理器上汇编程序生成如下代码: / +lActi

2、veRequests; mov eax, lActiveRequests add eax, 1 mov lActiveRequests, eax 上述代码的第三条指令被执行之前如果被同一CPU上的其它执行线程打断,或者在不同CPU上有完全相同的代码在同时运行都会引起+lActiveRequests的计数错误。解决的办法把load/add/store和load/subtract/store指令序列替换为原子指令: / +lActiveRequests; inc lActiveRequests / -lActiveRequests; dec lActiveRequests INC和DEC指令不能被

3、中断,但是多处理器环境中仍然是不安全的,因为这两个指令都是由几条微代码实现的。最终解决办法 / +lActiveRequests; lock inc lActiveRequests / -lActiveRequests; lock dec lActiveRequests LOCK指令前缀可以使当前执行多微码指令的CPU锁定总线,从而保证数据访问的完整性。两个最差的假定驱动程序开发者必须做如下两个最差的假定:1.操作系统可以在任何时间抢先任何例程并停留任何长的时间,所以我们不能保证自己的任务不被干扰或延迟。2.即使我们能防止被抢先,但其它CPU上执行的代码也会干扰我们代码的执行,甚至一个程序的代

4、码可以在两个不同线程的上下文中并发执行。 同步请求级一个确定的CPU上的活动仅能被拥有更高IRQL的活动抢先。IRQL与线程优先级线程优先级是与IRQL非常不同的概念。线程优先级控制着OS线程调度器的调度动作,决定何时抢先运行线程以及下一次运行什么线程。当IRQL级高于或等于DISPATCH_LEVEL级时线程切换停止,无论当前活动的是什么线程都将保持活动状态直到IRQL降到DISPATCH_LEVEL级之下。在进行线程调度时会切换线程上下文;按照IRQL进行活动抢先时不会切换线程上下文。利用IRQL进行同步方法:将所有对共享数据的访问都应该在同一(提升的,高于PASSIVE_LEVEL级的)

5、 IRQL上进行。上述方法只适用于单CPU。可利用KeRaiseIrql和KeLowerIrql函数改变当前IRQL。 KIRQL oldirql;ASSERT(KeGetCurrentIrql() QLock); .如何使用自旋锁当代码运行在低于或等于DISPATCH_LEVEL级时获取这个锁,并执行需要保护的代码,最后释放自旋锁。NTSTATUS DispatchSomething(.) KIRQL oldirql; PDEVICE_EXTENSION pdx = .; KeAcquireSpinLock(&pdx-QLock, &oldirql); . KeRelease

6、SpinLock(&pdx-QLock, oldirql);如何使用自旋锁如果知道代码已经处在DISPATCH_LEVEL级上 ,如DPC、StartIo,和其它执行在DISPATCH_LEVEL级上的驱动程序例程,可以调用两个专用函数来操作自旋锁 :KeAcquireSpinLockAtDpcLevel(&pdx-QLock);.KeReleaseSpinLockFromDpcLevel(&pdx-QLock);内核同步对象利用内核同步对象可以暂时阻塞一个线程的执行,同步不同线程的执行动作。内核同步对象仅影响OS线程调度器的调度动作,因此一般只在低于DISPATCH_

7、LEVEL级的代码中用于阻塞线程。在驱动程序中,只能在“非任意线程上下文非任意线程上下文”条件下利用内核同步对象阻塞调用者的线程或产生该请求的线程。在“任意线程上下文任意线程上下文”调用等待原语等待原语只会阻塞一个“无辜”的线程。非任意线程上下文如果驱动程序的回调例程能确切知道处于哪个线程上下文中,则称处于“非任意线程上下文”;大部分时间里,驱动程序无法知道这个事实,即处于“任意线程上下文”中。非任意线程上下文的例子:1. 设备的最高级驱动程序的IRP处理函数可以确切地知道它执行在发出该I/O请求的应用程序线程的上下文中。2.PNP类IRP的处理函数可以确切地知道它执行在一个系统线程(Syst

8、em Thread)中。3.在你自己创建的内核模式系统线程中。(PsCreateSystemThread)4.DriverEntry、AddDevice、DriverUnload等函数执行在一个系统线程(System Thread)中。常用的内核同步对象对象对象数据类型数据类型描述描述Event(事件) KEVENT阻塞一个线程直到检测到某事件发生 Semaphore(信号灯)KSEMAPHORE 控制多个线程对共享资源的访问Mutex(互斥)KMUTEX 执行到关键代码段时,禁止其它线程执行该代码段 Timer(定时器)KTIMER推迟线程执行一段时期Thread(线程) KTHREAD阻塞

9、一个线程直到另一个线程结束在单同步对象上等待在任何时刻,任何对象都处于两种状态中的一种:信号态信号态(signaled)或非信号态非信号态(not signaled) 。调用KeWaitForSingleObject或KeWaitForMultipleObjects函数可以使代码(以及背景线程)在一个或多个同步对象上等待,等待它们进入信号态。ASSERT(KeGetCurrentIrql() = DISPATCH_LEVEL);LARGE_INTEGER timeout;NTSTATUS status = KeWaitForSingleObject(object, WaitReason, Wa

10、itMode, Alertable, &timeout);KeWaitForSingleObject参数含义object 指向要等待的对象,它应该指向一个上面表中列出的同步对象。该对象必须在非分页内存中。WaitReason 是一个纯粹建议性的值,KWAIT_REASON枚举型,一般取值为Executive。WaitMode 是MODE枚举类型,该枚举类型仅有两个值:KernelMode和UserMode。一般取值为KernelMode 。Alertable 参数一般指定为FALSE。timeout 是一个64位超时值的地址,单位为100纳秒。正数的超时表示一个从1601年年1月月1日

11、日起的绝对时间。负数代表相对于当前时间的时间间隔。 指定为0将使等待函数立即返回。指定为NULL代表无限期等待。KeWaitForSingleObject(object, WaitReason, WaitMode, Alertable, &timeout);KeWaitForSingleObject返回值含义STATUS_SUCCESS,表示等待被满足。即你调用KeWaitForSingleObject时,对象或者已经进入信号态,或者在等待中进入信号态使等待返回。STATUS_TIMEOUT指出在指定的超时期限内对象未进入信号态 。如果指定0超时,则函数将立即返回。返回代码为STATU

12、S_TIMEOUT,代表对象处于非信号态,返回代码为STATUS_ SUCCESS,代表对象处于信号态。其它两个返回值STATUS_ALERTED和STATUS_USER_APC表示等待提前终止,对象未进入信号态 。在多个同步对象上等待objects指向一个指针数组,每个数组元素指向一个同步对象,count表示数组中指针的个数 。WaitType是枚举类型,其值可以为WaitAll或WaitAny,它指出你是等到所有对象都进入信号态,还是只要有一个对象进入信号态就可以。waitblocks参数指向一个KWAIT_BLOCK结构数组,内核用它来记录每个对象在等待中的状态。 不需要你对其进行初始化

13、。ASSERT(KeGetCurrentIrql() = DISPATCH_LEVEL);LARGE_INTEGER timeout;NTSTATUS status = KeWaitForMultipleObjects(count, objects, WaitType, WaitReason, WaitMode, Alertable, &timeout, waitblocks);KeWaitForMultipleObjects的返回值如果指定了WaitAll,则返回STATUS_SUCCESS表示等待的所有对象都进入了信号态。如果指定了WaitAny,则返回值在数值上等于进入信号态的对

14、象在objects数组中的索引。如果碰巧有多个对象进入了信号态,则返回值仅代表其中的一个,可能是第一个也可能是其它。可以认为返回值等于STATUS_WAIT_0加上数组索引。NTSTATUS status = KeWaitForMultipleObjects(.);if (NT_SUCCESS(status) iSignalled = status - STATUS_WAIT_0; .内核事件(Event)对象用途:把一个特定的事件通知给一个等待中的线程。与该对象相关的内核服务函数如下:服务函数服务函数功能功能KeInitializeEvent初始化事件对象KeSetEvent把事件设置为信号

15、态,返回前一个状态KeResetEvent把事件设置为非信号态,返回前一个状态KeClearEvent把事件设置为非信号态,不报告以前的状态。KeReadStateEvent取事件的当前状态。通知事件与同步事件通知事件通知事件(notification event)有这样的特性,当它进入信号态后,它将一直处于信号态直到明确地把它重置为非信号态。因此,当通知事件进入信号态后,所有在该事件上等待的线程都被释放。同步事件同步事件(synchronization event):只要有一个线程被释放,该事件就被自动重置为非信号态。 ASSERT(KeGetCurrentIrql() = DISPATCH

16、_LEVEL);KEVENT event;KeInitializeEvent(event, EventType, initialstate);EventType是一个枚举值,可以为NotificationEvent或SynchronizationEvent。initialstate是布尔量,为TRUE表示事件的初始状态为信号态,为FALSE表示事件的初始状态为非信号态。KeSetEvent函数调用KeSetEvent函数可以把事件置为信号态:ASSERT(KeGetCurrentIrql() lock, SynchronizationEvent, TRUE);void thread () Ke

17、WaitForSingleObject(&pdx-lock, Executive, KernelMode, FALSE, NULL); / do something KeSetEvent(&pdx-lock, EVENT_INCREMENT, FALSE);在应用层异步访问设备/ CreateFile的一个参数可以规定同步方式还是异步方式访问该设备的一个参数可以规定同步方式还是异步方式访问该设备hDevice = CreateFile(“.wdm1Device”, .);HANDLE waitEvent = CreateEvent(.);OVERLAPPED ol;ol.hEv

18、ent = waitEvent;ReadFile( hDevice, buffer, NumberOfBytesToRead, &ol);while(WaitForSingleObject(waitEvent, 100)=WAIT_TIMEOUT) if(!KeepRunning) CancelIo(hDevice); goto EXIT; / 从从buffer中访问数据中访问数据内核信号灯内核信号灯是一个有同步语义的整数计数器。信号灯计数器为正值时代表信号态,为0时代表非信号态。计数器不能为负值。释放信号灯将使信号灯计数器增1,在一个信号灯上等待将使该信号灯计数器减1。如果计数器值被

19、减为0,则信号灯进入非信号态,之后其它调用KeWaitXxx函数的线程将被阻塞。注意如果等待线程的个数超过了计数器的值,那么并不是所有等待的线程都可以恢复运行。服务函数与使用方法KeInitializeSemaphore:初始化信号灯对象KeReadStateSemaphore:取信号灯当前状态KeReleaseSemaphore:释放信号灯对象KSEMAPHORE semaphore;ASSERT(KeGetCurrentIrql() = PASSIVE_LEVEL);KeInitializeSemaphore(&semaphore, count, limit);.KeWaitFor

20、SingleObject(&semaphore, .);.ASSERT(KeGetCurrentIrql() = DISPATCH_LEVEL);KeReleaseSemaphore(semaphore, boost, delta, wait);互斥对象Mutex 互斥(mutex)就是mutual exclusion的简写。内核互斥对象为多个竞争线程串行化访问共享资源提供了一种方法。虽然用其它方法也能实现此功能,但互斥对象加入了一些措施能防止死锁死锁。如果互斥对象不被某线程所拥有,则它是信号态,反之则是非信号态。如果需要长时间串行化访问一个对象,应该首先考虑使用互斥(而不是依赖提升的

21、IRQL和自旋锁)。利用互斥对象控制资源的访问,可以使其它线程分布到多处理器平台上的其它CPU中运行,还允许导致页故障的代码仍能锁定资源而不被其它线程访问。 互斥对象的服务函数KeInitializeMutex 初始化互斥对象KeReadStateMutex 取互斥对象的当前状态KeReleaseMutex 设置互斥对象为信号态KMUTEX mutex;ASSERT(KeGetCurrentIrql() = PASSIVE_LEVEL);KeInitializeMutex(&mutex, level);.KeWaitForSingleObject(&mutex, .);.ASS

22、ERT(KeGetCurrentIrql() = PASSIVE_LEVEL);KeReleaseMutex(&mutex, wait);内核定时器 (Timer) Timer对象可以在指定的绝对时间或间隔时间后自动从非信号态变为信号态。它还可以周期性的进入信号态。可以利用KeWaitXxxx函数等待一个Timer对象在某个时间间隔后进入信号态,也可以利用Timer对象安排一个在某个时间间隔后或定期执行的DPC回调函数。定时器也分为通知型通知型和同步型同步型两种。通知型定时器及时结束后一直处于信号态,除非手动改变。因此,所有等待它的线程都被释放。同步定时器正相反,它只允许有一个等待线程

23、。一旦有线程在这种定时器上等待,并且开始执行,定时器就自动进入非信号态。内核定时器的服务函数服务函数服务函数功能功能KeInitializeTimer初始化一次性的通知型定时器KeInitializeTimerEx 初始化一次性的或周期性的通知型的或同步型定时器 KeSetTimer为通知型定时器设定时间或DPC对象KeSetTimerEx为定时器设定时间、周期和其它属性KeCancelTimer取消一个定时器KeReadStateTimer 获取定时器的当前状态。一次性定时器的用法KTIMER timer; / someone gives you thisASSERT(KeGetCurren

24、tIrql() = DISPATCH_LEVEL);KeInitializeTimerEx(&timer, NotificationTimer);/ KeInitializeTimer(timer);LARGE_INTEGER duetime;KeSetTimer(&timer, duetime, NULL);KeWaitForSingleObject(&timer, .);.周期性定时器的用法KTIMER timer; / someone gives you thisASSERT(KeGetCurrentIrql() = DISPATCH_LEVEL);KeIniti

25、alizeTimerEx(&timer, SynchronizationTimer);LARGE_INTEGER duetime;long period;KeSetTimerEx(&timer, duetime, period, NULL);while(True) KeWaitForSingleObject(&timer, .);.KeCancelTimer(&timer);定时器与DPCPKDPC dpc; / points to KDPC youve allocatedASSERT(KeGetCurrentIrql() = PASSIVE_LEVEL);KeInitializeTimer(timer);KeInitializeDpc(dpc, DpcRoutine, context);ASSERT(KeGetCurrentIrql() = DISPATCH_LEVEL);LARGE_INTEGER duetime;KeSetTimer(timer, duetime, dpc);. . . .VOID DpcRoutine(PKDPC dpc, PVOID context, .) .定时函数 KeDelayExecutionThread :可以在P

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论