PC机的高级编程技术

Download Report

Transcript PC机的高级编程技术

PC机的高级编程技术
1
第一节
软件对接口的访问方式
应用程序
驱动程序
实
模
式
操作系统
BIOS
直接访问
裸机
返回3页
返回4页
返回5页
返回6页
2
一、直接访问层次
特点:可以直接进行内存和端口的访问,也可以自
行决定是否在实模式和保护模式间切换。
通常所有端口和内存都是对程序员开放的。
工具:汇编语言或C语言。
应用:BIOS都是基于这种低级层次编写的;驱动程
序也有使用这种方法的;实模式下应用程序也可以使
用这种方法。
优点:编写的代码执行速度最快,占用空间最小。
缺点:需要对硬件和接口有很熟悉的了解;
熟悉汇编,或熟练使用C语言的指针;
不利于增加对新设备的控制。
转2页
3
二、BIOS访问层次
特点:通过BIOS提供的功能调用间接地对内存或端
口访问,从而控制硬件。
工具:汇编语言或C语言。
应用:驱动程序有些会使用这些功能调用;
需要获得高效率的应用程序也采用此方法。
优点:编写的代码执行速度较快;
编写的代码不需要直接对硬件进行控制。
缺点:需要对底层信号有所了解;
增加对新设备的控制不很方便,但好于低级
层次。
转2页
4
三、驱动程序层次
特点:使用BIOS功能调用、操作系统功能调用和直
接访问的方法编写符合特定操作系统管理规范的设备
驱动程序。
工具:VC++与DDK开发包,或第三方开发工具如
DriverStudio。
应用:在操作系统层面上的设备控制,并为应用程
序提供API支持。
优点:既能够控制硬件,又保证操作系统的完整与
安全。
缺点:需要对底层信号有所了解;
需要对操作系统各管理模块有深入的了解。
转2页
5
四、应用程序层次
特点:使用操作系统和各种驱动程序所提供的功能
调用或API函数间接对硬件或内存进行访问。
工具:VC++、Delphi、Java等。
应用:编写面向终端用户的各类应用程序。
优点:无需对硬件控制有太多了解,只需完成应用
层面的工作就可以,而且还保证操作系统的完整与安
全。
缺点:需要了解大量的API和功能调用函数的功能;
由于是间接调用,所以代码的效率和编译系
统有很大的关系。
转2页
6
第二节 Pentium 4的内存管理
一、实模式的存储管理(同PC/XT)
采用内存分段的办法,内存分为若干段,段的大小
根据需要决定,最大为64KB。
软件地址:
16位段地址
: 16位段内偏移
物理地址: (左移四位) 16位段地址
+
=
16位段内偏移
20位物理地址
思考:软件地址到物理地址变换的二义性及应用?
段地址大小设置与地址变换的关联?
7
内存区分配:
000000
640KB常规内存
0A0000
100000
384KB高端内存块
UMB
64KB高端内存区
HMA
110000
≈
FFFFFF
1~4095MB
扩展内存块
EMB
存放用户程序和DOS驻
留部分
存放显存、网卡和部分用户
的DOS驱动程序和BIOS
存放部分DOS驻留程序
大于1M以上的 扩展内存,
DOS下不能直接访问,需要用
≈
XMS规范使用。可利用DOS调
用或BIOS调用来使用。
8
二、保护模式下的系统地址寄存器
1、系统地址寄存器汇总表
名称
缩写
长度
内容
全局描述符表寄存器
GDTR
中断描述符表寄存器
IDTR
任务状态段寄存器
全局描述符表寄存器
TR
LDTR
48位 GDT的32位线性地址
+16位界限值
48位 IDT的32位线性地址
+16位界限值
16位 TSS的16位选择符
16位 LDT的16位选择符
说明:① GDTR、IDTR 在进入保护模式前设置,TR、LDTR在任
务切换时设置。
② GDT、IDT整个系统各一个,TSS、LDT每个任务一个。
③选择符与描述符含义不同。
9
2、系统地址寄存器与系统表关系
选择符
TR(16bit)
门描述符
…
…
界限
基址
GDTR(48bit)
描述符长均为8Byte
…
…
选择符
LDTR(16bit)
段描述符
TSS描述符
LDT描述符 2
1
0
GDT
界限
基址
IDTR(48bit)
门描述符 2
1
0
IDT
段寄存器选择符
选择符
段描述符
2
1
0
…
…
…
…
LDT描述符
选择符
界限
基址
TSS描述符
选择符
界限
基址
TSS结构
线性地址
LDT
返回下页
返回12页
返回13页
10
说明1:
GDT表项类型—LDT描述符、TSS描述符、段描述符;
IDT表项类型—中断门描述符、陷阱门描述符、任务门描
述符、调用门描述符;
LDT表项类型—段描述符。
说明2:
转19页
不同类型描述符通过第5字节的D3~D0区分。
说明3:
表或结构长度=(界限)+1。
转上页
例:GDTR的内容为00F0000000FFH,求GDT的首地址和末地址
及该表含有多少个描述符?
解:GDT首址=00F00000H;
GDT末址=00F00000H+0FFH=00F000FFH;
GDT描述符个数=GDT长度/8=(0FFH+1)/8=32。
11
3、TSS与LDT关系
TSS结构
I/O允许位映象(最大8K)
中断重定向位映象(32字节)
操作系统可利用(长度可变)
I/O允许位映象域基址
0
T
0
任务LDT选择符
0
GS、FS选择符
0
DS、SS选择符
0
CS、ES选择符
EDI、ESI、EBP、EBX、EDX、ECX、EAX
EFLAG、EIP
CR3
0
SS2(特权级2)
ESP2
0
SS1(特权级1)
ESP1
0
SS0(特权级0)
ESP0
0
反向链
TSS段限
68H
64H
60H
58H
50H
48H
28H
20H
1cH
18H
14H
10H
0cH
08H
04H
00H
虚拟8086模式且CR4中
VME=1时有效
当前任务LDTR
选择符
当前任务的段选择符
当前任务的优先级可改变,堆
栈的优先级也要相应改变。
系统为任务的0~2级堆栈指针
用SSi和ESPi表示。
转10页
上一任务TR
选择符
12
4、门描述符与段选择符关系
IDT
GDT/LDT
IDTR
门描述符
基地址
选择符
属性
偏移
段描述符
基地址
属性
边界
代码段
偏
移
EIP
段选择符
或TSS选择符
说明:利用段选择符的bit2确定查询GDT或LDT。
段选择符指向一个段描述符。
对任务门而言,选择符为TSS选择符。
中断服务
子程序
线性地址
转10页
任务的切换:通过直接改变TR方式,
或通过任务门间接方式实现。
13
三、保护模式下Pentium 4的段式存储管理
1、段式管理的地址变换
45
32
逻辑 段寄存器的15~2位
地址
段描述符
31
偏移量
0
段基址
段表
32位线性地址
物理
地址
Pentium 系列的虚拟地址空间是246=64TB。
注意:保护模式下段寄存器的含义已变为段选择符,
通常称段寄存器为段选择符。
14
2、段描述符表与段选择符
15
段选择符
2 1 0
TI RPL
索引
TI=1
段描述符
TI=0
段描述符
…
…
…
…
段描述符 2
1
0
LDT
段描述符 2
1
0
GDT
15
3、段描述符
D7
D0
段界限 7~0
段界限 15~8
基址 7~0
基址 15~8
P
DPL
G D/B 0
基址 23~16
S
TYPE
段界限 19~16
AVL
基址 31~24
返回20页
0
1
2
3
4
5
6
7
16
段描述符中的第6字节:
D7
G
D0
D/B
0
AVL
段界限 19~16
用户/操作系统可用位
D/B位
粒度位
代码段(D位)
D/B位
数据段(B位)
D=1 使用32位操作系统和32位寻址方式
D=0 使用16位操作系统和16位寻址方式
B=1 堆栈使用ESP寄存器,上限为FFFFFFFFH
B=0 堆栈使用SP寄存器,上限为FFFFH
思考:如何知道该段为代码段或数据段?
17
段描述符中非系统段的第5字节:
数据段标志
可写位
扩展方向位
D7
P
存在位
D0
DPL
S=1
特权位
E=0
E=1
ED
C
兼容位
代码段标志
W
A
R
访问位
可读位
S=1是非系统段
S=0是系统描述符
18
返回11页
段描述符中系统段的第5字节:
D7
P
D0
DPL
类型值
S
段类型
TYPE
类型值
段类型
未定义
0(0000)
未定义
8(1000)
1(0001)
286的有效任务状态段
9(1001)
2(0010)
LDT描述符
10(1010) 未定义
3(0011)
286的忙碌任务状态段
11(1011) 386的忙碌任务状态段
4(0100)
286的调用门
12(1100) 386的调用门
5(0101)
286或386的任务门
13(1101) 未定义
6(0110)
286的中断门
14(1110) 386的中断门
286的陷阱门
15(1111) 386的陷阱门
7(0111)
386的有效任务状态段
19
#include "stdafx.h"
#include <stdio.h>
#include <wtypes.h>
// wtypes.h定义了DWORDLONG,DWORD,WORD等数据类型
DWORDLONG gdtr,savegdt;
//下面是GDT中将创建的数据段描述符表,基地址0X00000F00,段界限为0XFFFF,优先级为
//3的在内存中的可写数据段,同P16段描述符
WORD descriptor[4]={0xFFFF,0X0F00,0XF200,0X0040};
int result[10];
int main(int argc, char* argv[])
{_asm
{
push
ebp
sgdt
gdtr
// 将GDTR寄存器的内容读取到gdtr开始的6个字节中,其中
// 前两个字节给出GDT的界限值,高4个字节给出GDT的基地址
mov
ebp,dword ptr [gdtr+2] // 将gdt的基地址读到EBP中
add
ebp,70h
// 我们选择70H偏移下的段描述符(GDT中第14个描述符)
lea
edi,savegdt
mov
esi,ebp
movsd
movsd
// 以上4条指令保存原来在70H偏移上的描述符
mov
edi,ebp
lea
esi,descriptor;
movsd
movsd
// 把我们的数据段描述符装入70H偏移上
push
es
mov
ax,0073h
// 选择字为描述符偏移70H拼接上低3位控制位元,
// 其中Ti为0,表示访问GDT,RPL为11,说明3级优先级,所以就为73H
转16页
20
}
_asm
{
a1:
}
_asm
{
a2:
}
_asm
{
mov
lea
mov
mov
es,ax
edi,result
eax,1
ebx,1
// ES装入选择字73H
// 将存放输出结果的变量的地址放在EDI中
mov
mov
add
loop
cx,10
es:[eax],eax
eax,4
a1
// 上面4条指令将向物理地址0X00000F00处写10个双字
mov
mov
mov
add
add
loop
cx,10
eax,es:[ebx]
[edi],eax
ebx,4
edi,4
a2
// 以上从物理地址0X00000F00处依次读出10个数据存放在
// result数组中
pop
pop
es
ebp
}
21
printf("result=");
for(int i=0;i<10;i++)
printf("%d,",result[i]);
return 0;
// 输出结果
}
22
四、保护模式下Pentium 4的虚拟页式存储管理
主存
程序1
页面
程序2
页框
程序3
~
~
~
~
23
1、页面大小选择
31
8
保留,缺省为全0
PAE
0
0
1
1
PSE
0
1
0
1
7
6
5
4
3
2
1
0
PCE PGE MCE PAE PSE DE TSD PVI VME CR4
物理地址位数 页面大小
4KB
32位
4KB/4MB
32位
4KB
36位
4KB/2MB
36位
24
2、32位4KB分页方式地址变换
31
32位线
性地址
22 21
页目录项号
12 11
0
偏移
页面号
CR3
32位物理地址
页表
页目录
31
页目
录项
12 11
页表基地址31~12
31
页表
项
8
AVL
12 11
页框基地址31~12
9
AVL
9
8
7
6
5
0
D A PCDPWT US RW P
7
6
5
4
4
3
3
2
2
1
0
1
0
D A PCDPWT US RW P
P=出现位,US=用户/监督位,PCD是页Cache禁止,D=Cache“脏”位,
RW=读/写位,PWT=页写贯穿位,A=访问位,AVL=用户的操作系统可用
位。而第7位(PS)在4KB分页中为0
25
3、 32位4MB分页方式地址变换
31
32位线性地址
22 21
0
偏移
页目录项号
CR3
32位物理地址
页目录
31
22
页目录项 页框基地址31~22
8
7
1
6
5
4
3
2
1
0
D A PCDPWT US RW P
26
4、36位4KB分页方式地址变换
31
CR3寄存
器
31
36 35
5 4
2 1 0
32字节对齐的PDPT PCDPW
T
基地址
12 11 6 5 4 3 2 1 0
PC PW
D T
4KB对齐的页目录基地址(高 AV
L
24位)
PDPT项
3
31
30 29
21 20
PDPT项号
页目录项号
P
12 11
0
偏移
页面号
32位线性地址
CR3
36位物理地址
页目录指针表
4×64位
63
36 35
页表
512×64位
12 11
9 8
7
0
4KB对齐的页表基地址 AVL
页目录项
63
页表项
36 35
页目录
512×64位
12 11
4KB对齐的页框基地址
AVL
9
8
G
7
0
6
5 4
3 2
1 0
A PCDPWT US RW P
6 5
4
3
2 1 0
D A PCDPWT US RW P
27
5、36位2MB分页方式地址变换
31
30 29
21 20
0
页目录项号
PDPT项号
偏移
32位线性地址
低21位
CR3
36位物理地址
高15位
页目录指针表
4×64位
63
页目录项
页目录
512×64位
36 35
21 20 12 11 9 8
7
6
AVL G
1
D A PCDPWT US RW P
2MB对齐的页框基地址
5
4
3
2
1
28
0
第三节 Windows 9x驱动程序设计
一、虚拟机与VxD的引入
应用软件
特权级3
操作系统提供的接口
操作系统内核与VxD
特权级0
计算机底层硬件
29
Windows 9x运行环境:
Windows 9x虚拟机环境
SYSVM
DOSVM
DOSVM
Win16程序
Win32程序
Win16程序
……
Win32程序
DOS程序
……
DOS程序
Win16程序
Win16程序
Win32地址空间
Win32地址空间
Win16地址空间
30
二、虚拟机下I/O端口访问
1、保护模式下I/O访问
执行in, out指令
是
CPL<IOPL?
IOPL用以表示指定的I/O操作处
于特权级的哪一级。它在EFLAGS
中。
CPL当前段的I/O优先级,它实
际上是CS段选择符的第0~第1位。
否
是
IOPM相关位=0?
否
产生一个一
般保护异常
进行I/O操作
约定:IOPL为0。
IOPM是对所有VM都起作用
的权限机制,它以位(bit)
来代表每个端口。某位为1,
则该代表的端口被禁止访问;
某位为0,则允许访问该位所
代表的端口。
31
2、V86模式下I/O访问
约定:忽略CPL<IOPL的判断。
访问:符合保护模式下的I/O访问规则。
执行in, out指令
是
IOPM相关位=0?
否
产生一个一
般保护异常
进行I/O操作
如何捕获一个端口的访问?配置IOPM相关位
32
3、VxDs捕获的I/O端口
33
三、虚拟机下中断或异常的处理
软中断
否
外部中断和处理器异常
CPL≤门描述符
的DPL ?
是
访问各类门描述符
否
禁止访问
转移后代码段的
DPL≤CPL ?
是
执行0特权级别中的中断处理程序
约定:外部中断和异常处理的DPL为0;
软中断的DPL不变,为设定值。
34
特权切换:中断处理程序特权级为0;
转移后DPL≤CPL时,运行中断处理程序。
硬件中断
VPICD
Win32
程序
IRET
INT x
CALL
IRET
RET
VxD中
的回调
函数
异常处理程序
虚拟机
思考:如何使自己从用户级转到核心级?
利用保护模式下中断的处理流程。
35
例:
在IDT中构造一个中断门描述符,使它的DPL=3,
这样它就可以被用户级的程序访问(DPL≤CPL),
将该中断门描述符的段选择字设为028H,显然该选择
字对应的代码段在GDT中,由于OS代码段基地址为
00000000H,段界限为FFFFFFFFH,所以现在中断
门的偏移量就实际给出了中断处理程序入口的线性地
址,我们只要把一个过程作为中断处理程序,这个过
程就处在核心级里了。
程序如下:
36
#include "stdafx.h"
#include <stdio.h>
#include <wtypes.h>
// wtypes.h定义了DWORDLONG,DWORD,WORD等数据类型
DWORD
_cr0;
// 用来保存CR0寄存器的值
void _declspec(naked) newint3(void)// 运行在核心级的中断3处理程序
{
_asm
{
mov
eax,cr0
// 这是必须在核心级才能执行的特权指令
mov
_cr0,eax
}
_asm
iretd
// 中断返回
}
int main(int argc, char* argv[])
{DWORDLONG idtr,saveidt;
WORD
newgate[4]={0x0000,0x0028,0xee00,0x0000}; // 中断门描述符
_asm
{
sidt
idtr
// 将IDTR的值存在idtr变量开始的6个字节中
mov
ebx,DWORD ptr[idtr+2] // 把IDT的基地址读入EBX寄存器
add
ebx,24 // 选择中断3作为进入核心级的入口,中断3的门描述符
// 的地址是IDT基地址加上3*8(每个门描述符8个字节)
mov
esi,ebx
lea
edi,saveidt
movsd
movsd
// 保存原来中断3的门描述符到saveidt中
lea
eax,newint3
37
转下页
mov
shr
mov
lea
mov
movsd
movsd
int
lea
mov
movsd
movsd
newgate,ax
eax,16
[newgate+6],ax
esi,newgate
edi,ebx
3h
esi,saveidt
edi,ebx
//向新的中断描述符中填入中断处理程序的偏移量
// 用新中断描述符在IDT中替换原来的中断3描述符
// 触发中断3,使程序跳转到0级执行中断处理程序
// 恢复原来中断3的门描述符
}
// 输出结果
printf("cr0=0x%x",_cr0);
return 0;
}
中断门描述符:
31
16 15 14 13 12
偏移量31~16
P
DPL
段选择符(28H—C,30H--D)
8 7 6 5 4
01110
000
0
保留
偏移量15~00
38
返回上页
四、虚拟设备驱动程序(VxD)基础
1、VxD的程序文件结构
VxD包含五个段:
VxD_CODE(保护模式下的代码段):含设备驱动程序回调例程、
服务程序、API接口函数和控制程序。
VxD_DATA(保护模式下的数据段):包含设备描述块、服务表、
全局变量等。
VxD_ICODE(保护模式下的初始化代码段):初始化时用的服务
程序和过程,初始化后被丢弃。
VxD_IDATA(保护模式下的初始化数据段):初始化时用的数据,
初始化后被丢弃。
VxD_REAL_INIT(实模式下的初始化资料与代码):初始化时调
用,该过程返回后被丢弃。
每个VxD装入主存后产生一个对象,每种型号的设备
39
共用一个VxD对象。
2、VxD的设备描述符块DDB
typedef
struct tagDDB {
DWORD
DDB_Next;
// VMM使用这一项来指出下一个DDB的地址
WORD
DDB_SDK_Version; // 建立该VxD所使用的SDK/DDK的版本号
WORD
DDB_Req_Device_Number;// 设备ID。UNDEFINED_DEVICE_ID
// 表示不使用唯一ID
BYTE
DDB_Dev_Major_Version;// VxD的主版本号
BYTE
DDB_Dev_Minor_Version;// VxD的次版本号
WORD
DDB_Flags;
// DDB标志位
BYTE
DDB_Name[8]; // VxD的名字,不足8个字节必须以空格补满
DWORD
DDB_
Init_Order;// 指定VxD的初始化顺序,如果没有特别的初
// 始化要求就使用 UNDEFINED_INIT_ORDER
DWORD
DDB_Control_Proc;
// 设备控制程序的地址
DWORD
DDB_V86_API_Proc;
// V86API程序的入口地址
DWORD
DDB_PM_API_Proc;
// 保护模式API程序的入口地址
DWORD
DDB_V86_API_CSIP
// V86入口点的CS:IP
DWORD
DDB_PM_API_CSIP;
// 保护模式入口点的CS:IP
DWORD
DDB_Reference_Data;
// 实模式初始化代码设置的参考资料
DWORD
DDB_VxD_Service_Table_Ptr; // VxD服务表的地址
DWORD
DDB_VxD_Service_Table_Size;// VxD服务表中提供的VxD服务的
// 数目
} DDB;
每个设备(含同型号设备)均有一个DDB。
40
3、VxD的加载与卸载
加载:使设备与Win32 API挂钩。
动态加载:
API函数
发的消息
功能
CreateFile
Sys_Dynamic_Device_Init
加载VxD
DeviceIOControl
CloseHandle
与VxD进行交互
处理
Sys_Dynamic_Device_Exit
卸载VxD
W32_DeviceIOControl
hDevice=CreateFile("\\\\.\\myfirst.vxd", 0, 0, 0, OPEN_EXISTING,
FILE_FLAG_DELETE_ON_CLOSE, 0);
DeviceIoControl(hDevice,DIOC_MY_IO, NULL, 0, NULL, 0,
NULL,NULL);
CloseHandle(hDevice);
41
静态加载:
42
4、VMM与VxD间的消息
5、Win32与VxD间的通信
Win32 应用程序
调用指令
回调函数
1.Shell_PostMessage()给应用程序消息
2.VWIN32_QueueUserApc()给应用线程事件
3.发通过转换后的Ring0级事件给应用程序
DeviceIoControl()
VxD
43
第四节 Windows 2000/XP设备驱动
程序设计
一、Windows 2000/XP的设备驱动程序
虚拟设备
驱动程序(VDD)
文件系统
驱动程序
保留设备
驱动程序
内核模式
驱动程序
PnP
驱动程序
显示
驱动程序
WDM驱动程序
类驱动程序
迷你驱动程序
44
虚拟设备驱动程序(Virtual Device Driver,VDD)
可使DOS应用程序访问x86平台上的硬件,也可支持
Windows 9x下的对端口访问。
WDM驱动程序是一种遵循电源管理协议并能在Win98
和Win2000上实现源代码级兼容的PnP驱动程序。WDM
驱动程序又可分为类驱动程序(管理已定义类的设备
)和迷你驱动程序(提供厂商专有的支持)。
显示驱动程序是用于显示和打印设备的内核模式驱
动程序。
文件系统驱动程序在本地磁盘或网络上实现标准PC
文件系统模型(含多层次目录结构和命名文件概念)。
保留设备驱动程序主要包括Windows NT早期版本的
驱动程序,它直接控制一个硬设备而不用其他驱动程
序帮助,可以不做修改地在Windows 2000中运行。
45
二、WDM的基本结构
1、设备对象及设备对象栈
设备对象:系统为帮助软件管理硬件而创建的一个数
据结构(包括PDO、FDO、FiDO)。
硬件仅指某功能设备,多功能设备有几个设备对象。
PDO(物理设备对象):设备对象中的物理型对象。
FDO(功能设备对象):设备对象中的功能型对象。
FiDO(过滤器设备对象):在I/O管理器、FDO和PDO间的
监视、修改IRP流的过滤型对象,分上层过滤和下层过
滤对象。
设备对象栈:描述对设备对象内部处理请求的驱动层
次的栈型结构,是设备对象的内部属性。
转下页
46
应用程序
Win32子系统
用户态
核心态
I/O系统服务
设备对象
FiDO
设
备
对
象
栈
上层过滤器驱动程序
FDO
功能驱动程序
FiDO
下层过滤器驱动程序
PDO
总线驱动程序
返回上页
返回下页
返回49页
IRP
I/O
管理器
返回50页
47
2、设备驱动程序
WDM模型中,设备驱动程序有下列几种类型:
功能驱动程序 :管理FDO所代表的设备,负责其初
始化、处理I/O操作、I/O操作完成时产生中断事件,
为用户提供一种适当的设备控制方式。
总线驱动程序:负责管理PDO硬件和计算机之间的
连接。
过滤器驱动程序:管理FiDO所代表的设备,用于监
视和修改IRP流,硬件或软件人员可利用过滤器驱动
程序修改上级驱动程序的传递过来的操作。
每种硬件设备的驱动程序由上述两到三种驱动程序
组成(可以无过滤器驱动程序)。
相同类型的总线共用一种总线驱动程序(如PCI总
线驱动程序)。
48
转上页
3、设备对象与设备驱动程序
a.功能设备与设备对象
每个功能设备对应一个设备对象(设备对象栈为设
备对象内部属性),相同型号的多个功能设备对应多
个设备对象。
每个设备对象具有不同的私有属性值(如设备ID)。
b.设备驱动程序与设备驱动程序对象
设备驱动程序加载时建立一个设备驱动程序对象。
c.设备对象与驱动程序
相同类型的多个设备对象共用一个过滤驱动程序和
功能驱动程序,即指向同一个驱动程序对象。
转47页
49
4、应用程序对设备对象的操作过程
操作接口:设备驱动程序的Win32 API函数,或重
载的设备驱动程序的Win32 API回调函数。
操作参数:设备句柄、操作类型及其它信息。
例:DeviceIoControl(hDevice, DIOC_MY_IO, NULL, 0,
NULL, 0, NULL,NULL);
Win32子系统处理:将I/O操作转换后交I/O管理器
处理, I/O管理器创建一个I/O请求包(IRP)后,送到
设备对象栈的最上层设备对象对应的驱动程序。
驱动程序处理:每层驱动程序都可决定如何处理
IRP,既可直接处理完该IRP就不再向下传,也可处
理完后继续向下传递,还可只做向下传递工作。下层
处理完的返回信息又通过该包的结构逐层向上传递。
转47页
50
5、设备驱动程序的装入过程
系统重启
硬件接入
添加新设备
检测
总线驱动程序检
测到新的硬件
指定
安装向导指定新
的硬件类型
取配置头区域信息
由指定得到信息
取得厂商、设备类型、版本、资源需求等信息
PnP管理器就创建PDO,在注册表中添加某些项
N
所有硬件检测完毕?
Y
PnP管理器决定分配哪些资源给各个设备
51
PnP管理器查找注册表中与该PDO相吻合的各级驱动程序
找到?
Y
N
读INF文件,按INF文件
指令安装各级驱动程序
PnP管理器查找所选驱动程序对象已在I/O管理器中
已装入?
Y
N
执行DriverEntry(),设置各例程的入
口地址,将对象指针装入I/O管理器中
PnP管理器装入最底层过滤驱动程序,调用其AddDevice函数,
该函数创建一个FiDO,实现FiDO与同级驱动程序的连接
52
AddDevice函数把PDO连接到FiDO上
PnP管理器依次装入各级驱动程序,完成整个设备对象栈
PnP管理器根据需要给设备发送各种PnP IRP
PnP管理器给设备发送“启动设备”PnP IRP,驱动程序将
分配的资源信息向下传送到设备配置头区域中,并启动设
备
驱动程序处于等待状态,等待IRP的到来
53
三、WDM驱动程序的结构
WDM驱动程序包含许多例程,操作系统调用这些例
程来执行对IRP的各种操作。
基本驱动程序例程
I/O控制例程
分发例程
DriverEntry
StartIO
DispatchPnp
AddDevice
AdapterControl
DispatchPower
OnInterrupt
DispatchWmi
DpcForIsr
DispatchRead
DispatchWrite
StartIO处理请求队列、 AdapterControl处理DMA
操作、OnInterrupt处理中断。
54
在每个WDM驱动程序中,下列例程必须有:
• DriverEntry例程:这个例程是每一个设备驱动程序
的入口。完成某些全局初始化(如将驱动程序对象
指针装入到I/O管理器中)工作,设置响应各种用户
请求的分发例程与I/O控制例程的入口。
• AddDevice例程:对于功能驱动程序,其AddDevice
函数的基本职责是创建一个设备对象并把它连接到
以PDO为底的设备堆栈中。
• DispatchPnp例程:用于处理IRP_MJ_PNP消息,以
便能实现即插即用的功能。
• DispatchPower例程:用于实现对电源管理的支持。
• DispatchWmi例程:WMI是微软实现的基于Web的
企业管理工业标准,该例程用于处理有关的消息。
55
驱动程序的DriverEntry函数示例:
extern "C"
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath)
{
// 初始化例程的入口地址
DriverObject->DriverUnload = DriverUnload;
DriverObject->DriverExtension->AddDevice = AddDevice;
DriverObject->DriverStartIo = StartIo;
DriverObject->MajorFunction[IRP_MJ_PNP] = DispatchPnp;
DriverObject->MajorFunction[IRP_MJ_POWER]=
DispatchPower;
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] =
DispatchWmi;
…… // 这里可加入其他MajorFunction处理例程的入口地址
返回66页
56
// 如果驱动程序需要访问设备的服务键,则备份
// RegistryPath
servkey.Buffer =(PWSTR)ExAllocatePool(PagedPool,
RegistryPath->Length+ sizeof(WCHAR));
if (!servkey.Buffer)
return STATUS_INSUFFICIENT_RESOURCES;
servkey.MaximumLength = RegistryPath->Length +
sizeof(WCHAR);
RtlCopyUnicodeString(&servkey, RegistryPath);
return STATUS_SUCCESS;
}
57
驱动程序的AddDevice函数示例:
NTSTATUS AddDevice(PDRIVER_OBJECT DriverObject,
PDEVICE_OBJECT pdo)
{
NTSTATUS status;
PDEVICE_OBJECT fdo;
status=IoCreateDevice(DriveObject,
sizeof(WDM_DEVICE_EXTENSION),
NULL,
FILE_DEVICE_UNKNOWN,
0,
FALSE,
&fdo); //在fdo中产生我们的功能设备对象
if (NT_ERROR(status))
return status;
……
}
58
驱动程序的IRP派遣函数示例:
NTSTATUS DispatchXxx(PDEVICE_OBJECT device,
PIRP Irp)
{
PIO_STACK_LOCATION stack =
IoGetCurrentIrpStackLocation(Irp);//获得栈单元指针
PDEVICE_EXTENSION pdx =
(PDEVICE_EXTENSION) device->DeviceExtension;
//获得设备扩展
……
//其他IRP处理操作
return STATUS_Xxx;
//返回状态码
}
59
四、I/O请求包(IRP)
定义:I/O请求包(IRP)是驱动程序操作的中心,
它是一个内核“对象”,是预先定义的数据结构。
应用:I/O管理器通过IRP对设备对象进行操作。
I/O管理器接收到一个I/O请求后,分配并初始化
一个IRP,再把它传递到合适的设备驱动程序中的最
高驱动程序中。
设备对象属性(IRP的参数)为设备驱动程序处理
的数据。
1、IRP组成
由一个固定的首部和一个可变数目的I/O栈组成。
60
61
I/O栈:通过IO_STACK_LOCATION结构数组实现。
一次I/O请求可能对应多个I/O操作的实现方法:
较多个IRP实现;
一个IRP实现+I/O栈实现。(较优)
IO_STACK_LOCATION结构成员:
MajorFunction(该IRP的主功能码)
MinorFunction(该IRP的副功能码)
Parameters(IRP参数)
DeviceObject(与该栈单元对应的设备对象地址)
FileObject(内核文件对象地址)
CompletionRoutine(I/O完成程序地址)
Context(任意的与上下文相关的值)。
62
Parameters成员的几个常见的类型:
IRP_MJ_CREATE请求,创建设备映射;
IRP_MJ_CLOSE请求,关闭设备映射;
IRP_MJ_READ请求,读取设备对象的信息;
IRP_MJ_WRITE请求,对设备对象写信息;
IRP_MJ_PNP请求,实现PnP管理,如
IRP_MN_START_DEVICE请求,启动设备;
IRP_MJ_IOCTL请求,I/O控制。
63
2、IRP创建与处理
创建者:I/O管理器,或其他的驱动程序。
创建IRP的函数:
IoBuildAsynchronousFsdRequest();//创建异步IRP
IoBuildSynchronousFsdRequest();//创建同步IRP
IoBuildDeviceIoControlRequest();
//创建同步IRP_MJ_DEVICE_CONTROL或
// IRP_MJ_INTERNAL_DEVICE_CONTROL请求
IoAllocateIrp();//创建其他种类IRP
IoMakeAssociatedIrp();//创建某些IRP的子IRP
64
IRP的设置与派遣:
I/O管理器在创建完IRP后,将I/O请求信息设置到
IRP中,并将IRP发送到设备驱动程序中。
已知参数信息:
PDEVICE_OBJECT DeviceObject; //设备对象结构
IRP_MJ_Xxx;
//I/O请求对应的功能码
I/O管理器的处理流程:
PIO_STACK_LOCATION stack =
IoGetNextIrpStackLocation(Irp);
//获得该IRP第一个堆栈单元的指针(堆栈初始为空)
stack->MajorFunction = IRP_MJ_Xxx;
//填充MajorFunction代码
……
//对栈做其他初始化的工作
//如StartIo()
NTSTATUS status = IoCallDriver(DeviceObject,Irp);
//把IRP发送到设备驱动程序 65
IoCallDriver的执行过程:
NTSTATUS IoCallDriver(PDEVICE_OBJECT device, PIRP Irp)
{
IoSetNextIrpStackLocation(Irp);
PIO_STACK_LOCATION stack =
IoGetCurrentIrpStackLocation(Irp);//获得栈单元指针
stack->DeviceObject = device; //设置设备对象结构地址
ULONG fcn = stack->MajorFunction;//得到主功能号
PDRIVER_OBJECT driver = device->DriverObject;
//获得驱动程序对象的地址
return (*driver->MajorFunction[fcn])(device,Irp);
//利用主功能号调用相应的派遣函数
}
*driver-> MajorFunction[fcn]是函数指针,它所指向的派遣函数
是在DriverEntry例程中指定的。
转56页
66
3、IRP参数与Win32 API
大部分IRP参数可以和Win32函数对应起来:
IRP类型
IRP_MJ_CREATE
IRP_MJ_READ
IRP_MJ_WRITE
IRP_MJ_IOCTL
IRP_MJ_CLOSE
IRP_MN_START_DEVICE
Win32 API
CreateFile
ReadFile
WriteFile
DeviceIoControl
CloseFile
StartDevice
说明:IRP_MJ_PNP包含8个IRP_MN_xx消息,
IRP_MN_START_DEVICE就是其中的一个。
67
五、即插即用
支持即插即用主要是指实现一个AddDevice程序和一
个IRP_MJ_PNP处理程序。这个PnP IRP有8个次功能
代码(它们的主功能代码都是IRP_MJ_PNP)。
·
·
·
·
·
·
·
·
IRP_MN_START_DEVICE
IRP_MN_QUERY_REMOVE_DEVICE
IRP_MN_REMOVE_DEVICE
IRP_MN_CANCEL_REMOVE_DEVICE
IRP_MN_STOP_DEVICE
IRP_MN_QUERY_STOP_DEVICE
IRP_MN_CANCEL_STOP_DEVICE
IRP_MN_SURPRISE_REMOVAL
分配资源并启动设备
询问一个设备是否可以删除
设备被拔出,删除设备
取消查询删除请求
停止设备进行资源重新分配
询问设备是否可以停止
取消查询停止请求
用户在意外下拔出设备
68
NTSTATUS DispatchPnp(PDEVICE_OBJECT fdo, PIRP Irp)
{
PIO_STACK_LOCATION stack =
IoGetCurrentIrpStackLocation(Irp);
ULONG fcn = stack->MinorFunction; // 取副功能码
static NTSTATUS status=STATUS_SUCCESS
switch (fcn) // 按照副功能码调用相关的处理函数
{
case IRP_MN_START_DEVICE:
HandleStartDevice(fdo,Irp);break;
case IRP_MN_QUERY_REMOVE_DEVICE:
HandleQueryRemove(fdo, Irp);break;
……
}
if (fcn >= arraysize(fcntab))
return DefaultPnpHandler(fdo, Irp);
69
return status;
}
六、数据读/写
WDM有4个标准的资源类型,分别是:
• CmResourceTypePort(端口资源)
• CmResourceTypeMemory(内存资源)
• CmResourceTypeInterrupt(中断资源)
• CmResourceTypeDma(DMA资源)
1、内存与端口访问
尽管PC机的I/O端口是单独编址的,但为了做到和
统一编址的机器兼容性,Windows 2000的设计者使用
了硬件抽象层(HAL)的概念。无论是单独编址还是
统一编址,只需要使用P386表中所给的函数就可以访
问端口和内存了。
70
2、响应中断
响应中断首先要配置中断,也就是截获中断,然后
就需要编写中断处理程序。
配置中断资源是在StartDevice函数中实现的,使用
从CmResourceTypeInterrupt描述符中得到的参数来调
用IoConnectInterrupt函数。和实模式下一样,在调用
IoConnectInterrupt进行中断配置前应该禁止PC机的中
断,调用之后再允许设备中断。
71
七、WDM驱动程序的安装文件
驱动程序的安装:
安装步骤:根据INF文件中的指令进行。
安装内容:将可执行文件复制到正确的位置(通
常是Windows System32\Drivers目录);
创建各种注册表项。
INF文件:
是一个文本文件,它由节组成,每一节从括在方
括号中的节名称开始,后面是节的内容,大部分段都
含有一系列“keyword = value”形式的项。
72
节
[Version]
项
Signature
值描述
ClassGuid
DriverVer
$Windows NT$, $Windows 95$
或$Chicago$
INF文件创建者
系统定义的类名字,或用户指定
的新的类名字
匹配的类GUID
驱动程序的版本号
[Strings]
%String%=”Value”
指定一个字符串
[Manufacturer]
%manufacturer%=
models
指定厂商名和对应的models节的
名称
[models]
指定产品名称、对应的install节的名称和硬件ID,0个
或多个兼容ID
Provider
Class
73
节
[install]
项
Copyfiles=@filename
| filelist
Addreg=addreg
ProfileItems
[Destinati DefaultDestDir=dirid,
onDirs]
[subdir]
filelist= dirid,
[subdir]
值描述
指定要复制的文件,或列出
filelist节的名称
指定addreg节的名称
列出指定要添加到“开始”菜单
中的项
对默认文件复制和filelist节中
的文件复制,指定目录ID和可选
的子目录。dirid是一个目录代码,
指示存放文件的标准位置。
Windows 2000DDK定义了这些代码,
如代码10表示Windows目录
[filelist] 要安装的文件列表
[addreg]
添加新的键和值
74