I/O 完成端口( Windows核心编程 )

 一个服务应用程序的结构可以有两种方式:

  •   在串行模式下,单个线程等待一个客户发出请求(通常是通过网络)。当来了请求后,线程醒来处理客户的请求。
  •   在并发模型下,单个线程等待客户发出请求,而后创建新线程来处理请求。当新线程处理客户请求时,起初的线程循环回去等待另一个客户请求。处理客户请求的线程处理完毕后终结。

 

  串行模型的问题在于它不能很好地处理好多个同时的请求,只适用于最简单的服务程序。Ping服务器是串行服务器的一个很好的例子。

 

  因此并发模型就是最普通的了。它为每个请求都创建了一个新线程。而且通过增加硬件能力,会很容易使它的性能提高。

 

  当并发模型实现在 NT 上时,微软 NT 小组注意到这些应用程序的性能没有预料得那么高。特别是有很多线程运行着的时候。因为所有这些线程都是可运行的(没有被挂起或等待什么事),微软意识到NT内核花了太多的时间来转换运行线程的上下文(context),而真正留给线程来做它们自己的工作的时间却被压缩了。

 

  // 这个情况可以以我的一个例子来说明,我曾经花了一个下午去兵马俑,结果来去花在路上的时间有4个小时,而在兵马俑只呆了40分钟。这个例子有点夸张,不过夸张有助于理解 ;)

 

  要使NT成为一个强大的服务器环境,微软就需要解决这个问题。解决的方法是一个称为I/O完成端口的内核对象,它首次在NT3.5中被引入。I/O完成端口的理论基础是并行运行的线程的数目必须有一个上限。500个同时的客户请求,并不意味着500个运行的线程。但并发运行的合适的线程数是多少呢?只要可运行的线程数多于CPU数,操作系统一定要花时间来进行线程上下文的切换的。

 

  并行模型的一个低效之处是为每一个客户请求创建了一个新线程。创建线程比起创建进程来开销要小,但也远不是没有开销。如果当应用程序初始化时创建了一个线程池,而这些线程在应用程序执行期间是空闲的,程序的性能就能进一步提高。I/O完成端口就使用线程池。
  I/O完成端口可能是Win32提供的最复杂的内核对象。要创建I/O完成端口,应调用 CreateIoCompletionPort:

 

  HANDLE CreateIoCompletionPort(HANDLE hFileHandle, HANDLE hExistingCompletionPort, DWORD dwCompletionKey, DWORD dwNumberOfConcurrentThreads);

 

  前三个参数只在把完成端口同设备相关联的时候才有用。如果不关联设备,只创建完成端口,那么前三个参数可以为:INVALID_HANDLE_VALUE,NULL,0。最后一个参数指示I/O完成端口同时能运行的最多线程数。如果为0,那么默认为机器上的CPU数。不过你可以用几个不同的值做实验来确定哪个值有最佳的性能。顺便说一句,这个函数是唯一一个创建了内核对象,而没有 LPSECURITY_ATTRIBUTES 参数的 Win32 函数。这是因为完成端口只应用于一个进程内。

  当你创建一个I/O完成端口时,内核实际上创建了5个不同的数据结构。

 

  第一个是设备列表。所有与完成端口相关联的设备都会出现在这个列表里,结构就是:

 

  当调用 CreateIoCompletionPort 关联设备时,表项就增加;当设备句柄被关闭时,表项被删除。

  设备可以是:一个文件,socket,邮件槽或管道等等。完成键可以自定义。

 

  第二个数据结构是一个I/O完成队列。当一个设备的异步I/O请求完成时,系统检查该设备是否关联了一个完成端口。如果是,系统就向该完成端口的I/O完成队列里加入完成的I/O请求项。该队列中的每条表项给出了传输的字节数,32位完成键,I/O请求的OVERLAPPED结构的指针和一个错误码。

  当I/O请求完成时或当PostQueuedCompletionStatus被调用时,表项被增加;当“等待线程队列”中删除一条表项时,表项被删除。

  当服务应用程序初始化时,它应该创建I/O完成端口,而后应该创建一个线程池来处理客户请求。现在的问题在于池中应该有多少线程。这是一个很难回答的问题。一个标准的答案是将计算机上的CPU的数目乘以2。

  池中的所有线程应该执行同一个线程函数。一般说来,该线程函数执行一些初始化后进入一个循环,该循环在服务进程终止时才结束。在循环中,线程使自己睡眠来等待完成端口的设备I/O请求的完成。这是通过 GetQueuedCompletionStatus 来实现的:

 

  BOOL GetQueuedCompletionStatus(HANDLE hCompletionPort, LPDWORD lpdwNumberOfBytesTransferred, LPDWORD lpdwCompletionKey, LPOVERLAPPED* lpOverlapped, DWORD dwMilliseconds);

 

  第一个参数指出线程要监视哪个完成端口。很多服务应用程序只使用一个I/O完成端口,所有的I/O请求完成通知都发给了该端口。简单地说,GetQueuedCompletionStatus 使调用线程进入睡眠,直到指定的完成端口的I/O完成队列中出现了一项或直到超时。

 

  I/O完成端口的第三个数据结构是 等待的线程队列

 

  当线程池中的一个线程调用 GetQueuedCompletionStatus 时,调用线程的 ID 就被放入等待线程队列中。这样,I/O完成端口对象总是知道哪个线程正在等待处理完成的I/O请求。当完成队列里出现一项时,完成端口就唤醒等待线程队列里的一个线程,并把所有信息通过参数传过去。

  要注意如何处理 GetQueuedCompletionStatus 的返回:

  


  
  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
http://wangfaqiang.download.csdn.net/ 上面这个网址就可以看到所有7个的下载链接 内容简介   本书从Windows内核编程出发,全面系统地介绍了串口、键盘、磁盘、文件系统、网络等相关的Windows内核模块的编程技术,以及基于这些技术实现的密码保护、防毒引擎、文件加密、网络嗅探、网络防火墙等信息安全软件的核心组件的具体编程。主要知识重点包括:Windows串口与键盘过滤驱动、Windows虚拟存储设备与存储设备过滤驱动、Windows文件系统过滤驱动、文件系统透明加密/解密驱动、Windows各类网络驱动(包括TDI过滤驱动及三类NDIS驱动),以及最新的WDF驱动开发模型。有助于读者熟悉Windows内核驱动的体系结构,并精通信息安全类的内核编程技术。本书的大部分代码具有广泛的兼容性,适合从Windows 2000 一直到目前最新的Windows 7 Beta 版。   本书适合大专院校计算机系的学生、普通Windows程序员、Windows内核程序员、信息安全行业的程序员,以及希望了解Windows系统底层知识的计算机编程爱好者使用。阅读本书,需要读者有C语言、数据结构、操作系统和计算机网络的基础知识。 目录: 封面 -25 扉页 -24 内容简介 -23 序 -22 关于本书作者和贡献者 -20 前言 -18 阅读注意 -16 目录 -12 正文 1 第1章 内核上机指导 1 1.1 下载和使用WDK 2 1.1.1 下载安装WDK 2 1.1.2 编写第一个C文件 3 1.1.3 编译一个工程 5 1.2 安装与运行 6 1.2.1 下载一个安装工具 6 1.2.2 运行与查看输出信息 7 1.2.3 在虚拟机中运行 9 1.3 调试内核模块 9 1.3.1 下载和安装WinDbg 9 1.3.2 设置Windows XP 调试执行 10 1.3.3 设置Vista调试执行 11 1.3.4 设置VMWare的管道虚拟串口 11 1.3.5 设置Windows内核符号表 13 1.3.6 实战调试first 14 练习题 16 第2章 内核编程环境及其特殊性 17 2.1 内核编程的环境 18 2.1.1 隔离的应用程序 18 2.1.2 共享的内核空间 19 2.1.3 无处不在的内核模块 20 2.2 数据类型 21 2.2.1 基本数据类型 21 2.2.2 返回状态 22 2.2.3 字符串 23 2.3 重要的数据结构 23 2.3.1 驱动对象 23 2.3.2 设备对象 25 2.3.3 请求 26 2.4 函数调用 28 2.4.1 查阅帮助 28 2.4.2 帮助中有的几类函数 30 2.4.3 帮助中没有的函数 32 2.5 Windows的驱动开发模型 32 2.6 WDK编程中的特殊点 33 2.6.1 内核编程的主要调用源 33 2.6.2 函数的多线程安全性 34 2.6.3 代码的中断级 36 2.6.4 WDK中出现的特殊代码 37 练习题 38 第3章 串口的过滤 40 3.1 过滤的概念 41 3.1.1 设备绑定的内核API之一 41 3.1.2 设备绑定的内核API之二 43 3.1.3 生成过滤设备并绑定 43 3.1.4 从名字获得设备对象 45 3.1.5 绑定所有串口 46 3.2 获得实际数据 47 3.2.1 请求的区分 47 3.2.2 请求的结局 48 3.2.3 写请求的数据 49 3.3 完整的代码 50 3.3.1 完整的分发函数 50 3.3.2 如何动态卸载 52 3.3.3 完整的代码 53 本章的示例代码 53 练习题 54 第4章 键盘的过滤 56 4.1 技术原理 57 4.1.1 预备知识 57 4.1.2 Windows中从击键到内核 58 4.1.3 键盘硬件原理 60 4.2 键盘过滤的框架 61 4.2.1 找到所有的键盘设备 61 4.2.2 应用设备扩展 64 4.2.3 键盘过滤模块的DriverEntry 65 4.2.4 键盘过滤模块的动态加载 66 4.3 键盘过滤的请求处理 68 4.3.1 通常的处理 68 4.3.2 PNP的处理 69 4.3.3 读的处理 70 4.3.4 读完成的处理 71 4.4 从请求中打印出按键信息 72 4.4.1 从缓冲区中获得KEYBOARD_INPUT_DATA 72 4.4.2 从KEYBOARD_INPUT_DATA中得到键 73 4.4.3 从MakeCode到实际字符 74 4.5 Hook分发函数 75 4.5.1 获得类驱动对象 76 4.5.2 修改类驱动的分发函数指针 77 4.5.3 类驱动之下的端口驱动 78 4.5

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值