前言 1
第1章 引言 1
1.1 系统总体结构 1
1.1.1 Windows NT的设计目标 1
1.1.2 Windows NT中的硬件特权级 1
1.1.3 操作系统的基础成分 2
1.1.4 Executive的内容 3
1.1.5 基操作系统的扩展 5
1.1.6 Win32子系统 6
1.2 内核模式I/O组件 7
1.2.1 I/O子系统的设计目标 7
1.2.2 Windows NT中的分层驱动程序 7
1.2.3 SCSI驱动程序 8
1.2.4 网络驱动程序 9
1.3 专用驱动程序体系结构 11
1.3.1 视频驱动程序 11
1.3.2 打印机驱动程序 12
1.3.3 多媒体驱动程序 14
1.3.4 遗留16位应用的驱动程序 15
1.4 小结 16
第2章 硬件环境 17
2.1 硬件基础知识 17
2.1.1 设备寄存器 17
2.1.2 访问设备寄存器 18
2.1.3 设备中断 19
2.1.4 数据传输机制 20
2.1.5 直接内存存取(DMA)机制 21
2.1.6 设备专用内存 21
2.1.7 自动配置的要求 22
2.1.6 自动配置的要求 22
2.2 总线与Windows NT 23
2.2.1 ISA——工业标准体系结构 23
2.2.2 MCA——微通道体系结构 25
2.2.3 EISA——扩展的工业标准总线 27
2.2.4 PCI——外围部件互连 30
2.3 处理硬件的提示 32
2.3.1 了解硬件 32
2.3.2 使用硬件智能 33
2.3.3 测试硬件 33
2.4 小结 33
3.1.2 中断 34
3.2.1 CPU优先级 34
3.2 Windows NT的中断使用 34
3.1.3 内核模式线程 34
3.1.1 异常 34
3.1 内核模式代码的执行 34
第3章 内核模式I/O处理 34
3.2.2 中断处理次序 35
3.2.3 软件产生的中断 35
3.3 推迟过程调用(DPC) 36
3.3.1 DPC的操作 36
3.3.2 DPC的行为 36
3.4 访问用户缓冲区 37
3.5 内核模式驱动程序的结构 38
3.5.1 驱动程序初始化和清理例程 38
3.5.2 I/O系统服务Dispatch例程 39
3.5.3 数据传输例程 39
3.5.4 资源同步回调 40
3.5.5 其他驱动程序例程 40
3.6.1 数据传输 41
3.6.2 驱动程序的请求预处理 41
3.6 I/O处理次序 41
3.6.1 Windows NT的请求预处理 41
3.6.4 驱动程序的后处理 42
3.6.5 I/O管理器的后处理 42
3.7 小结 43
第4章 驱动程序和内核模式对象 44
4.1 数据对象与Windows NT 44
4.1.1 Windows NT和OOP 44
4.1.2 NT对象和Win32对象 44
4.2 I/O请求包(IRP) 44
4.2.1 IRP结构 45
4.2.2 操纵IRP 46
4.3 驱动程序对象 47
4.4 Device对象与Device Extension 48
4.4.2 操纵Device对象 49
4.4.1 Device对象的结构 49
4.4.3 Device Extension 50
4.5 Controller对象和Controller Extension 50
4.5.1 Controller对象的结构 51
4.5.2 操纵Controller对象 51
4.5.3 Controller Extension 52
4.6 Adapter对象 52
4.6.1 Adapter对象的结构 52
4.6.2 操纵Adapter对象 52
4.7 Interrupt对象 53
4.7.1 Interrupt对象的结构 53
4.7.2 操纵Interrput对象 53
4.8 小结 54
5.1.2 使用渐进开发方法 55
5.1.1 使用正规设计方法 55
5.1 驱动程序设计策略 55
第5章 一般的开发问题 55
5.1.3 使用样板驱动程序 56
5.2 编码约定和技术 56
5.2.1 一般建议 56
5.2.2 命名约定 56
5.2.3 头文件 57
5.2.4 状态返回值 58
5.2.5 Windows NT驱动程序支持例程 58
5.2.6 丢弃初始化例程 59
5.2.7 控制驱动程序分页 60
5.3 驱动程序内存分配 60
5.3.1 对驱动程序可用的内存 60
5.3.2 使用内核堆栈 61
5.3.3 使用池区域 61
5.3.4 内存再分配的系统支持 62
5.4 Unicode字符串 64
5.4.1 Unicode字符串数据类型 64
5.4.2 处理Unicode 64
5.5 中断同步 65
5.5.1 问题 65
5.5.2 中断阻塞 65
5.5.3 阻塞中断的规则 66
5.5.4 使用推迟过程调用的同步 66
5.6 同步多个CPU 66
5.6.1 自旋锁工作 66
5.6.2 使用自旋锁 66
5.6.3 使用自旋锁的规则 67
5.7 链表 68
5.7.1 单向链表 68
5.7.2 双向链表 69
5.7.3 删除链表块 69
5.8 小结 70
6.1 写DriverEntry例程 71
6.1.1 执行上下文环境 71
6.1.2 DriverEntry例程的工作 71
第6章 初始化和清理例程 71
6.1.3 初始化DriverEntry点 72
6.1.4 创建Device对象 72
6.1.5 选择缓冲策略 73
6.1.6 NT和Win32设备名 73
6.2 范例代码:驱动程序初始化 74
6.3 写Reiruitiailze例程 81
6.4 写Unload例程 82
6.4.1 执行上下文 82
6.4.2 Unload例程的作用 82
6.3.2 Reinitiailze例程的作用 82
6.3.1 执行上下文 82
6.5 范例代码:驱动程序清理 83
6.6 写Shutdown例程 86
6.6.1 执行环境 86
6.6.2 Shutdown例程的作用 86
6.6.3 启用关闭通知 86
6.7.2 WINOBJ实用程序 87
6.7.1 测试过程 87
6.7 驱动程序的测试 87
6.8 小结 88
第7章 硬件初始化 89
7.1 查找自动检测的硬件 89
7.1.1 自动检测如何工作 89
7.1.2 自动检测硬件和Registry 89
7.1.3 查询硬件数据库 90
7.1.4 ConfigCallback例程的作用 92
7.1.5 使用配置数据 93
7.1.6 转换配置数据 94
7.2 范例代码:查找自动检测硬件 94
7.2.1 XXDRIVER.H 95
7.2.2 AUTOCON.C 96
7.3 查找未识别的硬件 103
7.3.1 把驱动程序参数加到Registry 103
7.3.2 从Registry中检索参数 104
7.3.3 设备信息的其他来源 105
7.4 范例代码:查询Registry 106
7.5 分配和释放硬件 115
7.5.1 如何进行资源分配 115
7.5.2 如何声明拥有硬件资源 116
7.5.3 如何释放硬件 118
7.5.5 装入设备微代码 119
7.5.4 映射设备内存 119
7.6 范例代码:分配硬件 120
7.7 小结 124
第8章 驱动程序Dispatch例程 125
8.1 启用驱动程序Dispatch例程 125
8.1.1 I/O请求派发机制 125
8.1.2 启用特定的功能代码 126
8.1.3 决定支持哪些功能代码 126
8.2 扩展派发接口 126
8.2.1 定义私有的IOCTL值 127
8.2.2 IOCTL参数传递方法 127
8.2.3 编写IOCTL头文件 128
8.3 写驱动程序Dispatch例程 130
8.3.1 执行环境 130
8.3.2 Dispatch例程的作用 130
8.3.3 退出Dispatch例程 131
8.4 处理特定种类的请求 132
8.4.1 处理读写请求 133
8.4.2 处理IOCTL请求 133
8.4.3 管理IOCTL缓冲区 135
8.5 测试驱动程序Dispatch例程 136
8.5.1 测试过程 136
8.5.2 测试程序范例 137
8.6 小结 137
第9章 编程I/O数据传输 138
9.1 编程I/O的工作 138
9.1.1 编程I/O过程中发生什么 138
9.1.2 同步各种驱动程序例程 139
9.2 驱动程序初始化和清理 139
9.2.1 初始化StartI/O入口点 139
9.2.3 连接到中断源 140
9.2.2 初始化DpcForIsr例程 140
9.2.4 从中断源断开 141
9.3 写Start I/O例程 142
9.3.1 执行上下文 142
9.3.2 Start I/O例程的工作 142
9.4 写中断服务例程(ISR) 142
9.4.1 执行上下文 142
9.4.2 中断服务例程的工作 143
9.5 写Dpc Forlsr例程 143
9.5.1 执行上下文 143
9.5.2 DpcForlsr例程的工作 144
9.5.3 优先级增量 144
9.6 硬件:并行端口 145
9.6.1 并行端口的工作 145
9.6.2 设备寄存器 145
9.6.4 并行端口的驱动程序 146
9.6.3 中断行为 146
9.7 范例代码:并行端口驱动程序 147
9.7.1 XXDRIVER.H 147
9.7.2 INIT.C 147
9.7.3 TRANSFER.C 149
9.8 测试数据传输例程 155
9.9 小结 155
第10章 定时器 156
10.1 处理设备超时 156
10.1.1 I/O定时器例程的工作 156
10.1.2 如何截获设备超时条件 156
10.2 范例代码:截获设备超时 157
10.2.1 XXDRIVFR.H 157
10.2.2 INIT.C 158
10.2.3 TRANSFER.C 158
10.2.4 TIMER.C 161
10.3 管理没有中断的设备 162
10.3.1 处理无中断设备 163
10.3.2 CustomTimer Dpc例程的工作 163
10.3.3 如何建立CustomTimerDpc例程 164
10.3.4 如何指定到期时间 164
10.3.5 CustomTimerDpc例程的其他使用 165
10.4 范例代码.:基于定时器的驱动程序 165
10.4.1 XXDRIVER.H 165
10.4.2 INIT.C 166
10.4.3 TRANSFER.C 167
10.5 小结 171
第11章 全双工驱动程序 172
11.1 同时做两件事情 172
11.1.1 处理并发的IRP 172
11.1.4 实现辅路径 173
11.1.3 全双工驱动程序的数据结构 173
11.1.2 修改的驱动程序体系结构的工作 173
11.2 使用Device Queue对象 174
11.2.1 Device Queue对象的工作 174
11.2.2 如何使用Device Queue对象 175
11.3 写CustomDpc例程 176
11.3.1 如何使用CustomDpc例程 176
11.3.2 执行上下文 177
11.4 取消I/O请求 177
11.4.1 取消IRP的过程 178
11.4.2 同步问题 178
11.4.3 Cancel例程的工作 179
11.4.4 Dispatch Cleanup例程的工作 180
11.5.1 16550UART的工作 182
11.5.2 设备寄存器 182
11.5 硬件:16550 UART 182
11.5.3 中断行为 183
11.6 范例代码:全双工UART驱动程序 185
11.6.1 目标 185
11.6.2 XXDRIVER.H中的DEVICE EXTENSION 185
11.6.3 DISPATCH.C 186
11.6.4 DEVQUEUE.C 188
11.6.5 INPUT.C 191
11.6.6 ISR.C 193
11.6.7 CANCEL.C 197
11.7 小结 201
第12章 DMA驱动程序 202
12.1 Windows NT下的DMA如何工作 202
12.1.1 使用Adapter对象隐藏DMA硬件的变化 202
12.1.2 解决映射寄存器的分散/集中问题 203
12.1.3 用内存描述符表管理I/O缓冲区 204
12.1.4 维护Cache一致性 205
12.1.5 DMA驱动程序分类 206
12.1.6 NT DMA体系结构的限制 207
12.2 使用Adapter对象 207
12.2.1 找到正确的Adapter对象 207
12.2.2 获取和释放Adapter对象 209
12.2.3 设置DMA硬件 210
12.2.4 清仓Adaper对象Cache 211
12.3 写基于包的从属DMA驱动程序 212
12.3.1 基于包的从属DMA的工作 212
12.3.2 分割DMA传输 213
12.4 范例代码:基于包的从属DMA驱动程序 215
12.4.1 XXDRIVER.H 215
12.4.2 REGOON.C 216
12.4.3 TRANSFER.C 217
12.5.1 设置总线主硬件 224
12.5 写基于包总线的主DMA驱动程序 224
12.5.2 带分散/集中支持的硬件 226
12.5.3 用IoMapTransfer建立分散/集中列表 228
12.6 写公共缓冲区从属DMA驱动程序 229
12.6.1 分配公共缓冲区 229
12.6.2 使用公共缓冲区从属DMA保持吞吐率 229
12.7 写公共缓冲区总线主设备DMA驱动程序 232
12.8 小结 233
第13章 设备错误的日志记录 234
13.1 Windows NT中的事件日志 234
13.1.1 决定日志记录什么 234
13.1.2 事件日志如何工作 234
13.2 处理消息 235
13.2.1 消息编码如何工作 235
13.2.2 写消息定义文件 236
13.2.3 一个小例子:XXMSG.MC 238
13.2.4 编译消息定义文件 240
13.2.5 给驱动程序增加消息资源 241
13.2.6 登记驱动程序为事件源 242
13.3 生成日志项 242
13.3.1 准备驱动程序进行错误日志记录 242
13.3.2 分配错误日志包 242
13.3.4 日志记录错误 244
13.4 范例:一个错误日志记录例程 244
13.5 小结 250
第14章 系统线程 251
14.1 系统线程 251
14.1.1 何时使用线程 251
14.1.2 创建和终止系统线程 251
14.1.4 系统工作者线程 252
14.1.3 管理线程优先级 252
14.2 线程同步 253
14.2.1 时间同步 253
14.2.2 一般同步 253
14.3 使用派发器对象 254
14.3.1 Event对象 255
14.3.2 在驱动程序之间共享Event对象 256
14.3.3 Mutex对象 256
14.3.4 Semaphore对象 257
14.3.5 Timer对象 258
14.3.6 Thread对象 259
14.3.7 Mutex的变种 260
14.3.8 同步死锁 261
14.4 范例代码:一个基于线程的驱动程序 262
14.4.1 驱动程序的工作过程 262
14.4.2 XXDRIVER-H中的DEVICE-EX-TENSION结构 262
14.4.3 INIT.C中的XxCreateDevice函数 263
14.4.4 DISPATCH.C中的XxDispatchRead-Write函数 265
14.4.5 THREAD.C 266
14.4.6 TRANSFER.C 268
14.5 小结 276
第15章 高层驱动程序 277
15.1 中间驱动程序概述 277
15.1.1 什么是中间驱动程序 277
15.1.2 应使用分层结构吗 277
15.2 编写分层驱动程序 278
15.2.1 分层驱动程序如何工作 278
15.2.2 分层驱动程序的初始化和清理 279
15.2.3 代码段:连接到另一个驱动程序 280
15.2.4 分层驱动程序的其他初始化考虑 281
15.2.5 分层驱动程序中的I/O请求处理 282
15.2.6 代码段:调用低层驱动程序 283
15.3 写I/O Completion例程 284
15.3.1 请求I/O Completion回调 284
15.3.2 执行上下文 285
15.3.3 I/O Completion例程的作用 285
15.3.4 代码段:一个I/O Cornpletion例程 286
15.4 分配另外的IRP 287
15.4.1 IRP的I/O堆栈再讨论 288
15.4.2 控制IRP堆栈的大小 288
15.4.3 用IoBuildSynchronousFsdRequest创建IRP 289
15.4.4 用IoBuildAsynchronousFsdRequest创建IRP 291
15.4.5 用IoBuildDeviceIoControIRequest创建IRP 292
15.4.6 从头建立IRP 293
15.4.8 记录驱动程序分配的IRP 296
15.5 写过滤器驱动程序 297
15.5.2 过滤器驱动程序中的初始化和清理 298
15.5.1 过滤器驱动程序如何工作 298
15.5.3 实际发生的操作 300
15.5.4 使挂接透明 300
15.6 范例代码:过滤器驱动程序 301
15.6.1 YYDRIVER.H-驱动程序数据结构 301
15.6.2 INIT.C-初始化代码 301
15.6.3 DISPATCH.C-过渡器Dispatch例程 306
15.6.4 COMPLETE.C-I/O Completion例程 309
15.7 写紧耦合驱动程序 312
15.7.1 紧耦合驱动程序如何工作 313
15.7.2 紧耦合驱动程序中的初始化和清理 313
15.7.3 紧耦合驱动程序中的I/O请求处理 314
15.8 小结 315
第16章 构造和安装驱动程序 316
16.1 构造驱动程序 316
16.1.1 BUILD的用途 316
16.1.2 如何构造驱动程序 317
16.1.3 写SOURCES文件 318
16.1.4 BUILD生成的日志文件 319
16.1.5 递归的BUILD操作 319
16.2.1 使用预编译头文件 320
16.2.2 在驱动程序中包含版本信息 320
16.2 其他BUILD时的活动 320
16.2.3 在BUILD中包含非标准组件 322
16.2.4 把驱动程序符号数据移入.DBG文件 323
16.3.1 如何手工安装驱动程序 324
16.3.2 驱动程序Registry项 324
16.3 安装驱动程序 324
16.3.3 标准驱动程序的最终用户安装 326
16.4 控制驱动程序装入次序 327
16.4.1 改变驱动程序的Start值 327
16.3.4 非标准驱动程序的最终用户安装 327
16.4.2 建立驱动程序之间明确的依赖关系 328
16.4.3 建立全局组依赖关系 328
16.4.4 控制组内的装入次序 330
16.5 小结 331
17.1 驱动程序测试指南 332
17.1.1 尚未试驱动程序的一般方法 332
第17章 测试和调试驱动程序 332
17.1.2 使用Microsoft硬件兼容性测试(HCT) 333
17.2 关于驱动程序错误的一些思考 334
17.2.1 驱动程序错误分类 334
17.2.2 重复产生驱动程序错误 335
17.2.3 减少调试的编码策略 335
17.3 阅读崩溃时显示的屏幕 336
17.3.1 系统崩溃时发生什么 336
17.2.4 跟踪驱动程序错误 336
17.3.2 STOP消息的结构 337
17.3.3 STOP消息解释 339
17.4 WINDBG概述 339
17.4.1 源代码调试的关键 340
17.4.2 几个WINDBG命令 340
17.5.2 开始分析 342
17.5.1 分析的目标 342
17.5.3 跟踪堆栈 342
17.5 分析崩溃转储 342
17.5.4 间接检查方法 345
17.5.5 使用DUMPEXAM发现崩溃 347
17.6.1 开始和结束调试会话 348
17.6.2 设置断点 348
17.6 交互式调试 348
17.7 写WINDBG扩展 349
17.6.4 使用打印语句 349
17.7.1 WINDBG扩展如何工作 349
17.6.3 设置硬断点 349
17.7.2 初始化和版本检查函数 350
17.7.3 写扩展命令 351
17.7.4 WINDBG帮助函数 351
17.8 范例代码:一个WINDBG扩展 352
17.8.1 XXDBG.C 352
17.7.5 构造和使用扩展DLL 352
17.8.3 SOURCES文件 357
17.8.4 样本输出 357
17.8.2 XXDBG.DEF 357
17.9.1 在驱动程序中留下调试代码 358
17.9.2 截获不正确的假设 358
17.9 其他调试技术 358
17.9.3 使用错误检查回调函数 359
17.9.4 截获内存遗漏 359
17.9.5 使用计数器,二进制位和缓冲区 360
17.10 小结 362
18.1.2 了解硬件 363
18.1.1 知道要达到的目标 363
18.1.3 探讨创造性的驱动程序设计 363
18.1 一般指南 363
第18章 驱动程序性能 363
18.2 Windows NT中的性能监视 364
18.1.5 计量所做的每件事情 364
18.2.1 一些术语 364
18.1.4 创造性地优化代码 364
18.2.2 性能监视系统如何工作 365
18.3 把计数器名字加到Registry 366
18.3.1 Registry中的计数器定义 366
18.2.3 驱动程序如何输出性能数据 366
18.3.2 写LODCTR命令文件 367
18.3.3 使用LODCTR和UNLODCTR 368
18.4 性能数据的格式 369
18.4.1 性能数据的总体结构 369
18.4.2 计数器的类型 371
18.4.3 带多个实例的对象 373
18.5 写数据收集DLL 374
18.5.1 数据收集DLL的内容 374
18.5.2 数据收集DLL中的错误处理 375
18.5.3 安装DLL 376
18.6 范例代码:数据收集DLL 377
18.6.1 XXPERF-C 377
18.6.2 构造和安装这个例子 385
18.7 小结 385
附录A 开发环境 386
A.1 硬件和软件要求 386
A.2 调试符号文件 387
A.3 在目标系统上启用崩溃转储 387
A.4 启用目标系统的调试客户程序 388
附录B 常见错误检查代码 390
B.1 驱动程序的常见问题 390
B.2 同步问题 391
B.3 破坏的驱动程序数据结构 391
B.4 内存问题 392
B.5 硬件故障 393
B.6 配置管理器和Registry问题 394
B.7 文件系统问题 395
B.8 系统初始化故障 395
15.4.7 为低层驱动程序建立缓冲区 396
B.9 内部系统故障 397
参考文献 398