25、Windows驱动程序的同步处理(1)-阿里云开发者社区

开发者社区> 开发与运维> 正文
登录阅读全文

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

简介: 驱动程序的同步处理 可重入,是指函数的执行结果不和执行顺序有关。同步机制很大程度上依赖于中断请求级。 IRQ编号 设备名称 用途 IRQ0 Tine 计算机系统计时器 IRQ1 KeyBoard 键盘...

驱动程序的同步处理

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

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

Reversed

建议保留给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个,02级别为软件中断,331级为硬件中断。031优先级别逐步递增。硬件中断请求级别称为设备中断请求级 DIRQLWindows大部分时间运行在软件中断请求级中。当设备中断来临时,操作系统提升IRQLDIRQL,并运行中断处理函数。当中断处理函数结束后,操作系统把IRQL降到原来的级别。

clip_image001

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中,获取自旋锁仅仅是将当前的IRQLPASSIVE_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

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

 

#include "windows.h"
#include "process.h"
#include "stddef.h"
#include "conio.h"

UNIT WINAPI Thread1(LPVOID para)
{
	printf("Enter Thread\n");
	HANDLE *phEvent = (HANDLE*)para;
	SetEvent(*phEvent);
	printf("Leave Thread1\n");
	return 0;
}

int main()
{
	HANDLE hEvent = CreateEvent(NULL, 0, FALSE, NULL);
	HANDLE hThread1 = (HANDLE)_beginthreadex(NULL, 0, Thread1, &hEvent, 0, NULL);
	WaitForSingleObject(hEvent, INFINITE);
	return 0;
}

示例代码 P228

4)信号灯

常见的同步对象。

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

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

HANDLE CreateSemaphore(

LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // SD

LONG lInitialCount, // initial count

LONG lMaximumCount, // maximum count

LPCTSTR lpName // object name

);

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

ReleaseSemaphore

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

 

代码

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

 

代码

1 #include "windows.h"
2 #include "process.h"
3 #include "stdio.h"
4
5 UINT WINAPI Thread1(LPVOID para)
6 {
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)等待线程完成

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

 

代码

1 #include "windows.h"
2 #include "process.h"
3 #include "stdio.h"
4
5 UINT WINAPI Thread(LPVOID para)
6 {
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;
21 }

 

示例代码 P232

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章