天天看点

25、Windows驱动程序的同步处理(1)

驱动程序的同步处理

可重入,是指函数的执行结果不和执行顺序有关。同步机制很大程度上依赖于中断请求级。

IRQ编号

设备名称

用途

IRQ0

Tine

计算机系统计时器

IRQ1

KeyBoard

键盘

IRQ2

RedirectI RQ9

与IRQ9相接,MPU-401 MDI使用该IRQ

IRQ3

COM2

串口设备

IRQ4

COM1

IRQ5

LPT2

建议声卡使用该IRQ

IRQ6

FDD

软驱传输控制用

IRQ7

LPT1

打印机传输控制用

IRQ8

CMOSAlert

即时时钟

IRQ9

RedirectI RQ2

与IRQ2相接。可设定给其他硬件使用

IRQ10

Reversed

建议保留给网卡使用该IRQ

IRQ11

建议保留给AGP显卡使用

IRQ12

PS/2 Mouse

按PS/2鼠标,若无也可以设定给其他硬件使用

IRQ13

FPU

协处理器用,例如FPU(浮点运算器)

IRQ14

Primary IDE

主硬盘传输控制用

IRQ15

Secondary lde

从硬盘传输控制用

图 PIC中断向量 P220

APIC 高级PIC(Programmable interrupt controller),共24个中断。

1、中断请求级

1)基本概念

Windows 把中断请求级IRQL分成了32个,0~2级别为软件中断,3~31级为硬件中断。0~31优先级别逐步递增。硬件中断请求级别称为设备中断请求级 DIRQL。Windows大部分时间运行在软件中断请求级中。当设备中断来临时,操作系统提升IRQL到DIRQL,并运行中断处理函数。当中断处理函数结束后,操作系统把IRQL降到原来的级别。

25、Windows驱动程序的同步处理(1)

图 IRQL P222

用户模式的代码是运行在最低优先级的passive_level;驱动程序的DriveEntry函数,派遣函数,AddDevice等函数一般都运行在PASSIVE_LEVEL级别,在必要时申请进入DISPATCH_LEVEL级别。

Windows 负责线程调度的组件运行在DISPATCH_LEVEL级别。当前线程运行完毕后,系统自动从PASSIVE_LEVEL提升到 DISPATCH_LEVEL级别,当切换完毕后,操作系统又从DISPATCH_LEVEL降回到PASSIVE_LEVEL级别。

驱动程序的StartIO函数和DPC(deferred procedure call)函数,中断服务例程也运行在DISPATCH_LEVEL级别。

线程运行在PASSIVE_LEVEL级别。如果提升到DISPATCH_LEVEL级别,则不会发生线程的切换,这是一种很常见的同步处理机制。

页故障允许在PASSIVE_LEVEL级别(出现页故障时,调用缺页机制,进行物理内存和磁盘文件进行切换),如果在DISPATCH_LEVEL或更高级别,则系统崩溃。对于DISPATCH_LEVEL或更高级别程序必须使用非分页内存。

2)控制IRQL提升与降低

主要用几个函数:

KeGetCurrentIrql

KeRaiseIrql

KeLowerIrql

1 VOID RasieIRQL_Test()

2 {

3 KIRQL oldirql;

4 ASSERT(keGetCurrentIrql() <= DISPATCH_LEVEL);

5 keRaiseIrql(DISPATCH_LEVEL, &oldirql);

6 //...

7   kelowrIrql(oldirql);

8 }

示例程序 P224

2、自旋锁

同步处理机制。不同于线程中的等待事件;操作系统会把等待某一个事件的线程处于休眠状态,CPU运行其它线程;而自旋锁不会切换到其它线程,而是让这个线程一直自旋等待。所谓自旋,就是一直不停的询问:是否可以获取自旋锁。

在单CPU中,获取自旋锁仅仅是将当前的IRQL从PASSIVE_LEVEL级别升到DISPATCH_LEVEL级别,多CPU中要复杂一点。驱动程序必须在不大于DISPATCH_LEVEL级别中使用自旋锁。

自旋锁一般作用是为各派遣函数实现间同步。尽量不要把自旋锁放在全局变量中,而应当把自旋锁放在设备扩展里。

示例 参见http://www.cnblogs.com/mydomain/archive/2010/10/18/1855118.html

KeAcquireSpinLock

KeInitializeSpinLock

KeAcquireSpinLockAtDpcLevel

3、用户模式下的同步对象

同步对象包括事件,互斥体,信号灯等。用户模式下的同步对象是对内核模式下的同步对象的再封装。

1)等待

WaitForSingleObject

WaitForMultipleObjects

2)开启多线程

CreateThread

[1]中推荐_beginthread

http://msdn.microsoft.com/en-us/library/kdzttdcb%28VS.80%29.aspx

3)事件

典型的同步对象。

CreateEvent

所有形如CreateXXX的Win32 API,如果第一个参数是lpEventAttributes,则这种API内部都会创建一个相应的内核对象;这种API返回一个句柄(32位无符号整数),OS通过这个句柄找到具体的内核对象。

示例代码 P228

4)信号灯

常见的同步对象。

一般同步对象有两种状态:激发状态和未激发状态。

信号灯有个计数器,代表N个灯,只要有一个灯亮着,就说明处于激发状态。

HANDLE CreateSemaphore(

LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // SD

LONG lInitialCount, // initial count

LONG lMaximumCount, // maximum count

LPCTSTR lpName // object name

);

lInitialCount ,如果初始值大于0,则处理激发状态。

ReleaseSemaphore

对信号灯执行一次等待操作,减少一个读数,相当于熄灭一个灯泡。

25、Windows驱动程序的同步处理(1)
25、Windows驱动程序的同步处理(1)

代码

1 #include "windows.h"

2 #include "process.h"

3 #include "stdio.h"

4

5 UINT WINAPI Thread1(LPVOID para)

6 {

7 printf("Enter Thread1\n");

8 HANDLE *hSemaphore = (HANDLE*)para;

9 Sleep(5000);

10 printf("Leave Thread1\n");

11 ReleaseSemaphore(*hSemaphore, 1, NULL);

12 return 0;

13 }

14  int main()

15 {

16 HANDLE hSemaphore = CreateSemaphore(NULL, 2, 2, NULL);

17 WaitForSingleObject(hSemaphore, INFINITE);

18 WaitForSingleObject(hSemaphore, INFINITE);

19 HANDLE hThread1 = (HANDLE)_beginthreadex(NULL, 0, Thread1, &hSemaphore, 0, NULL);

20 WaitForSingleObject(hSemaphore, INFINITE);

21 }

示例代码 P229

5)互斥体

用互斥体来避免多个线程使用一个资源。互斥体类似于同步事件;不同的是同一个线程可以递归获得互斥体,而同步事件不能。

如果线程获得互斥体时,互斥体此时的状态是未激发(nonsignaled),而释放互斥体时,互斥体的状态为激发态。

激发有点拗口,就是signaled,也就是接到了信号了。激发与未激发,都是相对于其它线程而言的。

CreateMutex

25、Windows驱动程序的同步处理(1)
25、Windows驱动程序的同步处理(1)

7 HANDLE *phMutex = (HANDLE*)para;

8 WaitForSingleObject(phMutex, INFINITE);

9 WaitForSingleObject(phMutex, INFINITE);//对于同一个线程,可以获得多次

10  

11 printf("Enter Thread1\n");

12 Sleep(5000);

13 printf("Leave Thread1\n");

14 ReleaseMutex(*phMutex);

15 return 0;

16 }

17

18 UINT WINAPI Thread2(LPVOID para)

19 {

20 HANDLE *phMutex = (HANDLE*)para;

21 WaitForSingleObject(phMutex, INFINITE);

22 WaitForSingleObject(phMutex, INFINITE);//对于同一个线程,可以获得多次

23  

24 printf("Enter Thread2\n");

25 Sleep(5000);

26 printf("Leave Thread2\n");

27 ReleaseMutex(*phMutex);

28 return 0;

29 }

30

31 int main()

32 {

33 HANDLE hMutex = CreateMutex(NULL, NULL, NULL);

34 WaitForSingleObject(hSemaphore, INFINITE);

35 WaitForSingleObject(hSemaphore, INFINITE);

36 HANDLE hThread1 = (HANDLE)_beginthreadex(NULL, 0, Thread1, &hMutex, 0, NULL);

37 HANDLE hThread2 = (HANDLE)_beginthreadex(NULL, 0, Thread2, &hMutex, 0, NULL);

38 Sleep(5000);

39 return 0;

40 }

示例代码 P230

6)等待线程完成

线程对象。每个对象同样有两种状态:运行之中时为未激发,当终止时为激发状态。

25、Windows驱动程序的同步处理(1)
25、Windows驱动程序的同步处理(1)

5 UINT WINAPI Thread(LPVOID para)

7 printf("Enter Thread2\n");

8 Sleep(5000);

9 return 0;

10 }

11

12 int main()

13 {

14 HANDLE hThread[2];

15 WaitForSingleObject(hSemaphore, INFINITE);

16 WaitForSingleObject(hSemaphore, INFINITE);

17 hThread[0] = (HANDLE)_beginthreadex(NULL, 0, Thread, &hMutex, 0, NULL);

18 hThread[1] = (HANDLE)_beginthreadex(NULL, 0, Thread, &hMutex, 0, NULL);

19 WaitForMultipleObjects(2, hThread, TRUE, INFINITE);

20 return 0;

示例代码 P232