pc 机的高级编程技术
DESCRIPTION
PC 机的高级编程技术. 应用程序. 驱动程序 操作系统. 实 模 式. BIOS. 直接访问. 裸机. 第一节 软件对接口的访问方式. 返回 3 页. 返回 4 页. 返回 5 页. 返回 6 页. 一、 直接访问层次. 特点: 可以直接进行内存和端口的访问,也可以自行决定是否在实模式和保护模式间切换。 通常所有端口和内存都是对程序员开放的。 工具: 汇编语言或 C 语言。. 应用: BIOS 都是基于这种低级层次编写的;驱动程序也有使用这种方法的;实模式下应用程序也可以使用这种方法。. 优点: 编写的代码执行速度最快,占用空间最小。 - PowerPoint PPT PresentationTRANSCRIPT
1
PC 机的高级编程技术
2
第一节 软件对接口的访问方式
裸机
BIOS 直接访问
驱动程序操作系统
实模式
应用程序
返回 3 页 返回 4 页 返回 5 页 返回 6 页
3
一、直接访问层次 特点:可以直接进行内存和端口的访问,也可以自行决定是否在实模式和保护模式间切换。 通常所有端口和内存都是对程序员开放的。 工具:汇编语言或 C 语言。 应用: BIOS 都是基于这种低级层次编写的;驱动程序也有使用这种方法的;实模式下应用程序也可以使用这种方法。 优点:编写的代码执行速度最快,占用空间最小。 缺点:需要对硬件和接口有很熟悉的了解; 熟悉汇编,或熟练使用 C 语言的指针; 不利于增加对新设备的控制。
转 2 页
4
二、 BIOS 访问层次 特点:通过 BIOS 提供的功能调用间接地对内存或端口访问,从而控制硬件。 工具:汇编语言或 C 语言。 应用:驱动程序有些会使用这些功能调用; 需要获得高效率的应用程序也采用此方法。 优点:编写的代码执行速度较快; 编写的代码不需要直接对硬件进行控制。 缺点:需要对底层信号有所了解; 增加对新设备的控制不很方便,但好于低级层次。
转 2 页
5
三、驱动程序层次 特点:使用 BIOS 功能调用、操作系统功能调用和直接访问的方法编写符合特定操作系统管理规范的设备驱动程序。 工具: VC++ 与 DDK 开发包,或第三方开发工具如 DriverStudio 。 应用:在操作系统层面上的设备控制,并为应用程序提供 API 支持。 优点:既能够控制硬件,又保证操作系统的完整与安全。 缺点:需要对底层信号有所了解; 需要对操作系统各管理模块有深入的了解。
转 2 页
6
四、应用程序层次 特点:使用操作系统和各种驱动程序所提供的功能调用或 API 函数间接对硬件或内存进行访问。 工具: VC++ 、 Delphi 、 Java 等。 应用:编写面向终端用户的各类应用程序。 优点:无需对硬件控制有太多了解,只需完成应用层面的工作就可以,而且还保证操作系统的完整与安全。 缺点:需要了解大量的 API 和功能调用函数的功能; 由于是间接调用,所以代码的效率和编译系统有很大的关系。
转 2 页
7
第二节 Pentium 4 的内存管理一、实模式的存储管理(同 PC/XT ) 采用内存分段的办法,内存分为若干段,段的大小根据需要决定,最大为 64KB 。
16 位段地址 16 位段内偏移:
16 位段地址16 位段内偏移
(左移四位) +
20 位物理地址=
软件地址:物理地址:
思考:软件地址到物理地址变换的二义性及应用? 段地址大小设置与地址变换的关联?
8
内存区分配:000000
0A0000
100000
110000
≈≈
FFFFFF
640KB 常规内存
384KB 高端内存块UMB
64KB 高端内存区HMA
1~4095MB扩展内存块
EMB
存放用户程序和 DOS驻留部分
存放显存、网卡和部分用户的 DOS 驱动程序和 BIOS
存放部分 DOS 驻留程序
大于 1M 以上的 扩展内存, DOS 下不能直接访问,需要用 XMS 规范使用。可利用DOS 调用或 BIOS 调用来使用。
9
二、保护模式下的系统地址寄存器
说明:① GDTR 、 IDTR 在进入保护模式前设置, TR 、 LDTR 在任
务切换时设置。 ② GDT 、 IDT 整个系统各一个, TSS 、 LDT 每个任
务一个。 ③ 选择符与描述符含义不同。
名称 缩写 长度 内容
全局描述符表寄存器 GDTR
48 位 GDT 的 32 位线性地址+16 位界限值
中断描述符表寄存器 IDTR 48 位 IDT 的 32 位线性地址+16 位界限值
任务状态段寄存器 TR 16 位 TSS 的 16 位选择符全局描述符表寄存器 LDT
R16 位 LDT 的 16 位选择符
1 、系统地址寄存器汇总表
10
基址界限
选择符TSS 描述符
……
线性地址TSS结构
2 、系统地址寄存器与系统表关系
基址界限
选择符LDT 描述
符
……
01
LDT
2段描述符
段寄存器选择符选择符
基址界限
IDTR(48bit)
……
01
门描述符
IDT
2门描述符
基址界限
GDTR(48bit)
TR(16bit)选择符
……
01
TSS 描述符
GDT
2LDT 描述符
段描述符
LDTR(16bit)选择符
描述符长均为8Byte
返回下页 返回 12 页 返回 13 页
11
说明 1 : GDT 表项类型— LDT 描述符、 TSS 描述符、段描述
符; IDT 表项类型—中断门描述符、陷阱门描述符、任务门描 述符、调用门描述符; LDT 表项类型—段描述符。
说明 3 : 表或结构长度 = (界限) +1 。
例: GDTR 的内容为 00F0000000FFH ,求GDT 的首地址和末地址及该表含有多少个描述符 ?
转上页
解: GDT首址 =00F00000H ; GDT末址 =00F00000H+0FFH=00F000FFH ; GDT 描述符个数 =GDT 长度 /8=(0FFH+1)/8=32 。
说明 2 : 不同类型描述符通过第 5字节的 D3~D0 区
分。
转 19 页
12
3 、 TSS 与 LDT 关系
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 反向链
00H04H08H0cH10H14H18H1cH20H28H48H50H58H60H64H68H
TSS 段限
TSS结构
当前任务 LDTR选择符
上一任务 TR选择符
当前任务的段选择符
当前任务的优先级可改变,堆栈的优先级也要相应改变。系统为任务的 0~2 级堆栈指针用 SSi 和 ESPi 表示。
虚拟 8086 模式且 CR4中 VME=1 时有效
转 10 页
13
4 、门描述符与段选择符关系
偏移属性
选择符
IDTRIDT
边界属性
基地址
GDT/LDT
线性地址
中断服务子程序
代码段基地址
偏移
EIP门描述符
段选择符或 TSS 选择符
段描述符
说明:利用段选择符的 bit2确定查询GDT 或LDT 。
段选择符指向一个段描述符。 对任务门而言,选择符为 TSS 选择符。任务的切换:通过直接改变 TR 方式,
或通过任务门间接方式实现。
转 10 页
14
三、保护模式下 Pentium 4 的段式存储管理 1 、段式管理的地址变换
段寄存器的 15~2位
偏移量45 32 31 0
段描述符
段表32 位线性地址 物理
地址
逻辑地址
段基址
Pentium 系列的虚拟地址空间是 246= 64TB 。注意:保护模式下段寄存器的含义已变为段选择符, 通常称段寄存器为段选择符。
15
2 、段描述符表与段选择符
RPLTI段选择符15 2 1 0
索引
TI=1 TI=0
……
01
GDT
2段描述符
段描述符……
01
LDT
2段描述符
段描述符
16
3 、段描述符D7 D0
段界限 7~0段界限 15~8
基址 7~0基址 15~8
基址 23~16
基址 31~24
TYPES
AVL
DPLPG D/B 0 段界限 19~16
0123
4
567
返回 20 页
17
D7 D0
AVLG D/B 0 段界限 19~16
用户 / 操作系统可用位 D/B 位粒度位
D/B 位
代码段 (D 位 )D=1 使用 32 位操作系统和 32 位寻址
方式D=0 使用 16 位操作系统和 16 位寻址方式
数据段 (B 位 )B=1 堆栈使用 ESP 寄存器,上限为
FFFFFFFFH B=0 堆栈使用 SP 寄存器,上限为 FFFFH
段描述符中的第 6字节:
思考:如何知道该段为代码段或数据段?
18
段描述符中非系统段的第 5字节:
AW
R
ED
C
E=0
E=1S=1DPLP
D7 D0
存在位 特权位
S=1 是非系统段S=0 是系统描述符
代码段标志
数据段标志
兼容位
可读位
访问位
扩展方向位
可写位
19
段描述符中系统段的第 5字节:
类型值 段类型 类型值 段类型0(0000) 未定义 8(1000) 未定义1(0001) 286 的有效任务状态段 9(1001) 386 的有效任务状态段2(0010) LDT 描述符 10(101
0)未定义
3(0011) 286 的忙碌任务状态段 11(1011)
386 的忙碌任务状态段
4(0100) 286 的调用门 12(1100)
386 的调用门
5(0101) 286 或 386 的任务门 13(1101)
未定义
6(0110) 286 的中断门 14(1110)
386 的中断门
7(0111) 286 的陷阱门 15(1111)
386 的陷阱门
D7 D0TYPESDPLP
返回 11 页
20
#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,savegdtmov esi,ebpmovsd movsd // 以上 4条指令保存原来在 70H 偏移上的描述符mov edi,ebplea esi,descriptor ;movsdmovsd // 把我们的数据段描述符装入 70H 偏移上push esmov ax,0073h // 选择字为描述符偏移 70H 拼接上低 3 位控制位
元 ,// 其中 Ti 为 0, 表示访问 GDT,RPL 为 11, 说明 3 级优先级 , 所以就为
73H
转 16 页
21
mov es,ax // ES装入选择字 73Hlea edi,result // 将存放输出结果的变量的地址放在 EDI中mov eax,1mov ebx,1
}_asm{ mov cx,10a1 : mov es : [eax],eax
add eax,4loop a1 // 上面 4条指令将向物理地址 0X00000F00处写 10 个双
字}_asm{ mov cx,10a2 : mov eax,es : [ebx]
mov [edi],eaxadd ebx,4add edi,4loop a2 // 以上从物理地址 0X00000F00处依次读出 10 个数据存放
在// result 数组中
}_asm{ pop es
pop ebp}
22
printf ( "result=" );for ( int i=0 ; i<10 ; i++ )printf ( "%d,",result[i] ); // 输出结果return 0 ;
}
23
四、保护模式下 Pentium 4 的虚拟页式存储管理
主存
~~
~~
页框
页面程序 1
程序 2
程序 3
24
1 、页面大小选择
PGEPCE
831 7 6 5 4 3 2 1 0
MCEPAE PSE DE TSD VMEPVI保留,缺省为全 0 CR4
PAE PSE 物理地址位数 页面大小0 0 32 位 4KB
0 1 32 位 4KB/4MB
1 0 36 位 4KB
1 1 36 位 4KB/2MB
25
2 、 32 位 4KB 分页方式地址变换 32 位线性地址
页目录项号 页面号 偏移
CR3
32 位物理地址
31 22 21 12 11 0
页目录 页表31 12 11 9 8 7 6 5 4 3 2 1 0
PWTPCD页表基地址 31~12 PRWUSAVL AD0页目录项
PWTPCD页框基地址 31~12 PRWUSAVL AD页表项
31 12 11 9 8 7 6 5 4 3 2 1 0
P=出现位, US= 用户 /监督位, PCD 是页 Cache禁止, D=Cache“脏”位, RW=读 / 写位, PWT=页写贯穿位, A= 访问位, AVL= 用户的操作系统可用位。而第 7 位( PS )在 4KB 分页中为 0
26
3 、 32 位 4MB 分页方式地址变换
32 位线性地址 页目录项号 偏移
CR3
32 位物理地址
31 22 21 0
页目录
31 22 8 7 6 5 4 3 2 1 0
PWTPCD页框基地址 31~22 PRWUSAD1页目录项
27
4 、 36 位 4KB 分页方式地址变换 31 5 4 3 2 1 0
PWT
PCD32字节对齐的 PDPT基地址
CR3 寄存器
31 36 35 12 11 6 5 4 3 2 1 0
PWT
PCD
PAVL
PDPT项
4KB 对齐的页目录基地址(高24 位)
32 位线性地址页目录项号 页面号 偏移
CR3
36 位物理地址
31 30 29 21 20 12 11 0
页目录 页表
PDPT项号
页目录指针表4×64 位 512×64 位 512×64 位
63 36 35 12 11 9 8 7 6 5 4 3 2 1 0
PWTPCD4KB 对齐的页表基地址 PRWUSAVL A0页目录项
PWTPCD4KB 对齐的页框基地址 PRWUSAVL AD页表项63 36 35 12 11 9 8 7 6 5 4 3 2 1 0
0G
28
5 、 36 位 2MB 分页方式地址变换
32 位线性地址页目录项号 偏移
CR3
36 位物理地址
31 30 29 21 20 0
页目录
PDPT项号
页目录指针表4×64 位 512×64 位
页目录项 PWTPCD2MB 对齐的页框基地址
PRWUSAVL AD
63 36 35 21 20 12 11 9 8 7 6 5 4 3 2 1 0
1G
低 21 位
高 15 位
29
第三节 Windows 9x 驱动程序设计 一、虚拟机与 VxD 的引入
计算机底层硬件
操作系统内核与 VxD
操作系统提供的接口
应用软件特权级 3
特权级 0
30
Windows 9x运行环境: Windows 9x虚拟机环境
SYSVM
Win16 地址空间
Win16 程序
Win16 程序
Win16 程序
Win16 程序
Win32 程序
Win32 地址空间
Win32 程序
Win32 地址空间
……
DOSVM
DOS 程序
DOSVM
DOS 程序……
31
二、虚拟机下 I/O 端口访问
执行 in, out 指令
CPL<IOPL ?
IOPM相关位 =0 ?
进行 I/O 操作
否
否
产生一个一般保护异常
是
是
IOPL 用以表示指定的 I/O 操作处于特权级的哪一级。它在EFLAGS中。
CPL当前段的 I/O 优先级,它实际上是 CS 段选择符的第 0~第 1 位。
IOPM 是对所有 VM 都起作用的权限机制,它以位( bit )来代表每个端口。某位为 1 ,则该代表的端口被禁止访问;某位为 0 ,则允许访问该位所代表的端口。
1 、保护模式下 I/O 访问
约定: IOPL 为 0 。
32
2 、 V86 模式下 I/O 访问
执行 in, out 指令
IOPM相关位 =0 ?
进行 I/O 操作
否
产生一个一般保护异常
是
约定:忽略 CPL< IOPL 的判断。 访问:符合保护模式下的 I/O 访问规则。
如何捕获一个端口的访问?配置 IOPM相关位
33
3 、 VxDs捕获的 I/O 端口
34
三、虚拟机下中断或异常的处理
外部中断和处理器异常 软中断
CPL≤门描述符的 DPL ?
是访问各类门描述符
转移后代码段的DPL≤CPL ?
是执行 0 特权级别中的中断处理程序
禁止访问
否
否
约定:外部中断和异常处理的 DPL 为 0 ; 软中断的 DPL 不变,为设定值。
35
特权切换:中断处理程序特权级为 0 ; 转移后DPL≤CPL 时,运行中断处理程序。
Win32
程序
虚拟机
VxD中的回调函数
VPICD
INT x CALL
RET
IRET
硬件中断
IRET
异常处理程序
思考:如何使自己从用户级转到核心级? 利用保护模式下中断的处理流程。
36
例: 在 IDT中构造一个中断门描述符,使它的DPL=3 ,这样它就可以被用户级的程序访问( DPL≤CPL ),将该中断门描述符的段选择字设为028H ,显然该选择字对应的代码段在 GDT中,由于OS 代码段基地址为 00000000H ,段界限为FFFFFFFFH ,所以现在中断门的偏移量就实际给出了中断处理程序入口的线性地址,我们只要把一个过程作为中断处理程序,这个过程就处在核心级里了。
程序如下:
37
#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,ebxlea edi,saveidtmovsdmovsd // 保存原来中断 3 的门描述符到 saveidt中lea eax,newint3 转下页
38
mov newgate,axshr eax,16mov [newgate+6],ax // 向新的中断描述符中填入中断处理程序的偏移量lea esi,newgatemov edi,ebxmovsdmovsd // 用新中断描述符在 IDT中替换原来的中断 3 描述符int 3h // 触发中断 3 ,使程序跳转到 0 级执行中断处理程序lea esi,saveidtmov edi,ebxmovsdmovsd // 恢复原来中断 3 的门描述符
}printf ( "cr0=0x%x",_cr0 ); // 输出结果return 0 ;
}
返回上页
P DPL 01110 000 保留
偏移量 15~00
偏移量 31~16
段选择符 (28H—C,30H--D)
31 16 15 14 13 12 8 7 6 5 4 0
中断门描述符:
39
四、虚拟设备驱动程序 (VxD) 基础1 、 VxD 的程序文件结构 VxD 包含五个段: VxD_CODE( 保护模式下的代码段 ) :含设备驱动程序回调例程、服务程序、 API 接口函数和控制程序。 VxD_DATA( 保护模式下的数据段 ) :包含设备描述块、服务表、全局变量等。 VxD_ICODE( 保护模式下的初始化代码段 ) :初始化时用的服务程序和过程,初始化后被丢弃。 VxD_IDATA( 保护模式下的初始化数据段 ) :初始化时用的数据,初始化后被丢弃。 VxD_REAL_INIT( 实模式下的初始化资料与代码 ) :初始化时调用,该过程返回后被丢弃。 每个 VxD装入主存后产生一个对象,每种型号的设备共用一个 VxD 对象。
40
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
// 表示不使用唯一 IDBYTE 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_ORDERDWORD DDB_Control_Proc ; // 设备控制程序的地址DWORD DDB_V86_API_Proc ; // V86API 程序的入口地址DWORD DDB_PM_API_Proc ; // 保护模式 API 程序的入口地址DWORD DDB_V86_API_CSIP // V86 入口点的 CS : IPDWORD DDB_PM_API_CSIP ; // 保护模式入口点的 CS : IPDWORD DDB_Reference_Data ; // 实模式初始化代码设置的参考资料DWORD DDB_VxD_Service_Table_Ptr ; // VxD服务表的地址DWORD DDB_VxD_Service_Table_Size ; // VxD服务表中提供的 VxD服务
的 // 数目
} DDB ; 每个设备(含同型号设备)均有一个 DDB 。
41
3 、 VxD 的加载与卸载
动态加载:
API 函数 发的消息 功能CreateFile Sys_Dynamic_Device_Init 加载 VxD
DeviceIOControl W32_DeviceIOControl 与 VxD 进行交互处理
CloseHandle Sys_Dynamic_Device_Exit 卸载 VxD
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 );
加载:使设备与 Win32 API挂钩。
42
静态加载:
43
4 、 VMM 与 VxD 间的消息 5 、 Win32 与 VxD 间的通信
Win32 应用程序
调用指令 回调函数
VxD
DeviceIoControl()1.Shell_PostMessage()给应用程序消息2.VWIN32_QueueUserApc()给应用线程事件3. 发通过转换后的 Ring0 级事件给应用程序
44
第四节 Windows 2000/XP 设备驱动程序设计
一、 Windows 2000/XP 的设备驱动程序
虚拟设备驱动程序( VDD )
内核模式驱动程序
文件系统驱动程序
保留设备驱动程序
PnP驱动程序
显示驱动程序
WDM 驱动程序
类驱动程序 迷你驱动程序
45
虚拟设备驱动程序 (Virtual Device Driver , VDD) 可使 DOS 应用程序访问 x86平台上的硬件,也可支持 Windows 9x 下的对端口访问。 WDM 驱动程序是一种遵循电源管理协议并能在Win98 和 Win2000 上实现源代码级兼容的 PnP驱动程序。 WDM 驱动程序又可分为类驱动程序(管理已定义类的设备)和迷你驱动程序(提供厂商专有的支持)。 显示驱动程序是用于显示和打印设备的内核模式驱动程序。 文件系统驱动程序在本地磁盘或网络上实现标准 PC文件系统模型 ( 含多层次目录结构和命名文件概念 ) 。 保留设备驱动程序主要包括Windows NT早期版本的驱动程序,它直接控制一个硬设备而不用其他驱动程序帮助,可以不做修改地在 Windows 2000中运行。
46
二、 WDM 的基本结构1 、设备对象及设备对象栈 设备对象 : 系统为帮助软件管理硬件而创建的一个数据结构(包括 PDO 、 FDO 、 FiDO )。 硬件仅指某功能设备,多功能设备有几个设备对象。 PDO( 物理设备对象 ): 设备对象中的物理型对象。 FDO( 功能设备对象 ): 设备对象中的功能型对象。 FiDO( 过滤器设备对象 ): 在 I/O 管理器、 FDO 和PDO 间的监视、修改 IRP流的过滤型对象,分上层过滤和下层过滤对象。 设备对象栈 : 描述对设备对象内部处理请求的驱动层次的栈型结构,是设备对象的内部属性。
转下页
47
FDO
设备对象FiDO
FiDO
PDO
设备对象栈
上层过滤器驱动程序
功能驱动程序
下层过滤器驱动程序
总线驱动程序
IRP
I/O 系统服务
Win32子系统
应用程序
用户态
核心态
I/O管理器
返回上页 返回下页 返回 49 页 返回 50 页
48
2 、设备驱动程序 WDM 模型中,设备驱动程序有下列几种类型: 功能驱动程序 :管理 FDO 所代表的设备,负责其初始化、处理 I/O 操作、 I/O 操作完成时产生中断事件,为用户提供一种适当的设备控制方式。 总线驱动程序:负责管理 PDO 硬件和计算机之间的连接。 过滤器驱动程序:管理 FiDO 所代表的设备,用于监视和修改 IRP流,硬件或软件人员可利用过滤器驱动程序修改上级驱动程序的传递过来的操作。 每种硬件设备的驱动程序由上述两到三种驱动程序组成(可以无过滤器驱动程序)。 相同类型的总线共用一种总线驱动程序(如 PCI总线驱动程序)。 转上页
49
3 、设备对象与设备驱动程序a. 功能设备与设备对象 每个功能设备对应一个设备对象(设备对象栈为设备对象内部属性),相同型号的多个功能设备对应多个设备对象。 每个设备对象具有不同的私有属性值 ( 如设备ID) 。
c. 设备对象与驱动程序 相同类型的多个设备对象共用一个过滤驱动程序和功能驱动程序,即指向同一个驱动程序对象。
b. 设备驱动程序与设备驱动程序对象 设备驱动程序加载时建立一个设备驱动程序对象。
转 47 页
50
4 、应用程序对设备对象的操作过程
Win32子系统处理:将 I/O 操作转换后交 I/O 管理器处理, I/O 管理器创建一个 I/O请求包 (IRP)后,送到设备对象栈的最上层设备对象对应的驱动程序。
转 47 页
操作接口:设备驱动程序的 Win32 API 函数,或重载的设备驱动程序的 Win32 API回调函数。 操作参数:设备句柄、操作类型及其它信息。 例: DeviceIoControl ( hDevice, DIOC_MY_IO, NULL, 0, NULL, 0, NULL,NULL );
驱动程序处理:每层驱动程序都可决定如何处理IRP ,既可直接处理完该 IRP 就不再向下传,也可处理完后继续向下传递,还可只做向下传递工作。下层处理完的返回信息又通过该包的结构逐层向上传递。
51
5 、设备驱动程序的装入过程
总线驱动程序检测到新的硬件
安装向导指定新的硬件类型
取得厂商、设备类型、版本、资源需求等信息取配置头区域信息 由指定得到信息
硬件接入 添加新设备系统重启检测 指定
PnP 管理器就创建 PDO ,在注册表中添加某些项
所有硬件检测完毕?N
Y
PnP 管理器决定分配哪些资源给各个设备
52
PnP 管理器查找注册表中与该 PDO相吻合的各级驱动程序
执行 DriverEntry() ,设置各例程的入口地址,将对象指针装入 I/O 管理器中
找到? N
Y 读 INF文件,按 INF文件指令安装各级驱动程序
PnP 管理器装入最底层过滤驱动程序,调用其AddDevice 函数,该函数创建一个 FiDO ,实现 FiDO与同级驱动程序的连接
PnP 管理器查找所选驱动程序对象已在 I/O 管理器中
已装入? N
Y
53
PnP 管理器根据需要给设备发送各种 PnP IRP
PnP 管理器给设备发送“启动设备” PnP IRP ,驱动程序将分配的资源信息向下传送到设备配置头区域中,并启
动设备驱动程序处于等待状态,等待 IRP 的到来
PnP 管理器依次装入各级驱动程序,完成整个设备对象栈
AddDevice 函数把 PDO连接到FiDO 上
54
三、 WDM 驱动程序的结构 WDM 驱动程序包含许多例程,操作系统调用这些例程来执行对 IRP 的各种操作。基本驱动程序例程 I/O 控制例程 分发例程
DriverEntry
AddDevice
StartIO
AdapterControlOnInterrupt
DpcForIsr
DispatchPnp
DispatchPowerDispatchWmi
DispatchRead
DispatchWrite
StartIO处理请求队列、 AdapterControl处理 DMA操作、 OnInterrupt处理中断。
55
在每个 WDM 驱动程序中,下列例程必须有:• DriverEntry例程:这个例程是每一个设备驱动程序
的入口。完成某些全局初始化(如将驱动程序对象指针装入到 I/O 管理器中)工作,设置响应各种用户请求的分发例程与 I/O 控制例程的入口。
• AddDevice例程:对于功能驱动程序,其 AddDevice函数的基本职责是创建一个设备对象并把它连接到以 PDO 为底的设备堆栈中。
• DispatchPnp例程:用于处理 IRP_MJ_PNP消息,以便能实现即插即用的功能。
• DispatchPower例程:用于实现对电源管理的支持。• DispatchWmi例程: WMI 是微软实现的基于 Web
的企业管理工业标准,该例程用于处理有关的消息。
56
驱动程序的 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 页
57
// 如果驱动程序需要访问设备的服务键,则备份 // 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 ;}
58
驱动程序的 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 ; ……}
59
驱动程序的 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 ; //返回状态码}
60
四、 I/O请求包( IRP ) 定义: I/O请求包( IRP )是驱动程序操作的中心,它是一个内核“对象”,是预先定义的数据结构。 应用: I/O 管理器通过 IRP 对设备对象进行操作。 I/O 管理器接收到一个 I/O请求后,分配并初始化一个 IRP ,再把它传递到合适的设备驱动程序中的最高驱动程序中。 设备对象属性( IRP 的参数)为设备驱动程序处理的数据。1 、 IRP组成 由一个固定的首部和一个可变数目的 I/O栈组成。
61
62
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 (任意的与上下文相关的值)。
63
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 控制。
64
2 、 IRP创建与处理 创建者: I/O 管理器,或其他的驱动程序。 创建 IRP 的函数: IoBuildAsynchronousFsdRequest();//创建异步 IRP IoBuildSynchronousFsdRequest();//创建同步 IRP IoBuildDeviceIoControlRequest(); //创建同步 IRP_MJ_DEVICE_CONTROL 或 // IRP_MJ_INTERNAL_DEVICE_CONTROL请求 IoAllocateIrp();//创建其他种类 IRP IoMakeAssociatedIrp();//创建某些 IRP 的子IRP
65
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 发送到设备驱动程序
66
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 页
67
3 、 IRP参数与 Win32 API
大部分 IRP参数可以和 Win32 函数对应起来:
IRP 类型 Win32 APIIRP_MJ_CREATE CreateFileIRP_MJ_READ ReadFileIRP_MJ_WRITE WriteFileIRP_MJ_IOCTL DeviceIoControlIRP_MJ_CLOSE CloseFile
IRP_MN_START_DEVICE
StartDevice
说明: IRP_MJ_PNP 包含 8 个 IRP_MN_xx消息, IRP_MN_START_DEVICE 就是其中的一个。
68
五、即插即用 支持即插即用主要是指实现一个 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 用户在意外下拔出设备
69
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) ; return status ; }
70
六、数据读 / 写WDM 有 4 个标准的资源类型,分别是:• CmResourceTypePort (端口资源)• CmResourceTypeMemory (内存资源)
• CmResourceTypeInterrupt (中断资源)
• CmResourceTypeDma ( DMA资源)1 、内存与端口访问 尽管 PC 机的 I/O 端口是单独编址的,但为了做到和统一编址的机器兼容性, Windows 2000 的设计者使用了硬件抽象层( HAL )的概念。无论是单独编址还是统一编址,只需要使用 P386 表中所给的函数就可以访问端口和内存了。
71
2 、响应中断 响应中断首先要配置中断,也就是截获中断,然后就需要编写中断处理程序。 配置中断资源是在 StartDevice 函数中实现的,使用从 CmResourceTypeInterrupt 描述符中得到的参数来调用 IoConnectInterrupt 函数。和实模式下一样,在调用 IoConnectInterrupt 进行中断配置前应该禁止 PC机的中断,调用之后再允许设备中断。
72
七、 WDM 驱动程序的安装文件 驱动程序的安装: 安装步骤:根据 INF文件中的指令进行。 安装内容:将可执行文件复制到正确的位置(通常是 Windows System32\Drivers目录); 创建各种注册表项。 INF文件: 是一个文本文件,它由节组成,每一节从括在方括号中的节名称开始,后面是节的内容,大部分段都含有一系列“ keyword = value”形式的项。
73
节 项 值描述[Version] Signature
ProviderClass
ClassGuidDriverVer
$Windows NT$, $Windows 95$或 $Chicago$INF文件创建者系统定义的类名字,或用户指定的新的类名字匹配的类 GUID驱动程序的版本号
[Strings] %String%=”Value”
指定一个字符串
[Manufacturer]
%manufacturer%=models
指定厂商名和对应的 models节的名称
[models] 指定产品名称、对应的 install 节的名称和硬件 ID , 0个或多个兼容 ID
74
节 项 值描述[install] Copyfiles=@filenam
e | filelistAddreg=addregProfileItems
指 定 要复制 的文件 , 或列出filelist 节的名称指定 addreg 节的名称列出指定要添加到“开始”菜单中的项
[DestinationDirs]
DefaultDestDir=dirid,[subdir]filelist= dirid,[subdir]
对默认文件复制和 filelist 节中的文件复制,指定目录 ID 和可选的子目录。 dirid 是一个目录代码,指示存放文件的标准位置。Windows 2000DDK 定 义 了这 些 代 码 , 如 代 码 10 表示Windows目录
[filelist] 要安装的文件列表[addreg] 添加新的键和值