完成端口(IOCP)编程探讨

article/2025/11/8 10:31:45
FW: http://www.zhuaxia.com/item/473350387

完成端口(IOCP)编程探讨

2007-08-26 16:06:00
来自:C++博客-首页原创精华区  
新建目录...
根目录
新手试用频道

         本文主要探讨一下windows平台上的完成端口开发及其与之相关的几个重要的技术概念,这些概念都是与基于IOCP的开发密切相关的,对开发人员来讲,又不得不给予足够重视的几个概念:
1) 基于IOCP实现的服务吞吐量
2)IOCP模式下的线程切换
3)基于IOCP实现的消息的乱序问题。

一、IOCP简介
    提到IOCP,大家都非常熟悉,其基本的编程模式,我就不在这里展开了。在这里我主要是把IOCP中所提及的概念做一个基本性的总结。IOCP的基本架构图如下:
 

如图所示:在IOCP中,主要有以下的参与者:
--》完成端口:是一个FIFO队列,操作系统的IO子系统在IO操作完成后,会把相应的IO packet放入该队列。
--》等待者线程队列:通过调用GetQueuedCompletionStatus API,在完成端口上等待取下一个IO packet。
--》执行者线程组:已经从完成端口上获得IO packet,在占用CPU进行处理。
除了以上三种类型的参与者。我们还应该注意两个关联关系,即:
--》IO Handle与完成端口相关联:任何期望使用IOCP的方式来处理IO请求的,必须将相应的IO Handle与该完成端口相关联。需要指出的时,这里的IO Handle,可以是File的Handle,或者是Socket的Handle。
--》线程与完成端口相关联:任何调用GetQueuedCompletionStatus API的线程,都将与该完成端口相关联。在任何给定的时候,该线程只能与一个完成端口相关联,与最后一次调用的GetQueuedCompletionStatus为准。
二、高并发的服务器(基于socket)实现方法
        一般来讲,实现基于socket的服务器,有三种实现的方式(thread per request的方式,我就不提了:)):
第一、线程池的方式。使用线程池来对客户端请求进行服务。使用这种方式时,当客户端对服务器的连接是短连接(所谓的短连接,即:客户端对服务器不是长时间连接)时,是可以考虑的。但是,如若客户端对服务器的连接是长连接时,我们需要限制服务器端的最大连接数目为线程池线程的最大数目,而这应用的设计本身来讲,是不好的设计方式,scalability会存在问题。
第二、基于Select的服务器实现。其本质是,使用Select(操作系统提供的API)来监视连接是否可读,可写,或者是否出错。相比于前一种方式,Select允许应用使用一个线程(或者是有限几个线程)来监视连接的可读写性。当有连接可读可写时,应用可以以non-bolock的方式读写socket上的数据。使用Select的方式的缺点是,当Select所监视的连接数目在千的数量级时,性能会打折扣。这是因为操作系统内核需要在内部对这些Socket进行轮询,以检查其可读写性。另一个问题是:应用必须在处理完所有的可读写socket的IO请求之后,才能再次调用Select,进行下一轮的检查,否则会有潜在的问题。这样,造成的结果是,对一些请求的处理会出现饥饿的现象。
        一般common的做法是Select结合Leader-Follower设计模式使用。不过不管怎样,Select的本质造成了其在Scalability的问题是不如IOCP,这也是很多high-scalabe的服务器采用IOCP的原因。
第三、IOCP实现高并发的服务器。IOCP是实现high-scalabe的服务器的首选。其特点我们专门在下一小姐陈述。
三、IOCP开发的几个概念
第一、服务器的吞吐量问题。
      我们都知道,基于IOCP的开发是异步IO的,也正是这一技术的本质,决定了IOCP所实现的服务器的高吞吐量。
       我们举一个及其简化的例子,来说明这一问题。在网络服务器的开发过程中,影响其性能吞吐量的,有很多因素,在这里,我们只是把关注点放在两个方面,即:网络IO速度与Disk IO速度。我们假设:在一个千兆的网络环境下,我们的网络传输速度的极限是大概125M/s,而Disk IO的速度是10M/s。在这样的前提下,慢速的Disk 设备会成为我们整个应用的瓶颈。我们假设线程A负责从网络上读取数据,然后将这些数据写入Disk。如果对Disk的写入是同步的,那么线程A在等待写完Disk的过程是不能再从网络上接受数据的,在写入Disk的时间内,我们可以认为这时候Server的吞吐量为0(没有接受新的客户端请求)。对于这样的同步读写Disk,一些的解决方案是通过增加线程数来增加服务器处理的吞吐量,即:当线程A从网络上接受数据后,驱动另外单独的线程来完成读写Disk任务。这样的方案缺点是:需要线程间的合作,需要线程间的切换(这是另一个我们要讨论的问题)。而IOCP的异步IO本质,就是通过操作系统内核的支持,允许线程A以非阻塞的方式向IO子系统投递IO请求,而后马上从网络上读取下一个客户端请求。这样,结果是:在不增加线程数的情况下,IOCP大大增加了服务器的吞吐量。说到这里,听起来感觉很像是DMA。的确,许多软件的实现技术,在本质上,与硬件的实现技术是相通的。另外一个典型的例子是硬件的流水线技术,同样,在软件领域,也有很著名的应用。好像话题扯远了,呵呵:)
第二、线程间的切换问题。
         服务器的实现,通过引入IOCP,会大大减少Thread切换带来的额外开销。我们都知道,对于服务器性能的一个重要的评估指标就是:System/Context Switches,即单位时间内线程的切换次数。如果在每秒内,线程的切换次数在千的数量级上,这就意味着你的服务器性能值得商榷。Context Switches/s应该越小越好。说到这里,我们来重新审视一下IOCP。
     完成端口的线程并发量可以在创建该完成端口时指定(即NumberOfConcurrentThreads参数)。该并发量限制了与该完成端口相关联的可运行线程的数目(就是前面我在IOCP简介中提到的执行者线程组的最大数目)。当与该完成端口相关联的可运行线程的总数目达到了该并发量,系统就会阻塞任何与该完成端口相关联的后续线程的执行,直到与该完成端口相关联的可运行线程数目下降到小于该并发量为止。最有效的假想是发生在有完成包在队列中等待,而没有等待被满足,因为此时完成端口达到了其并发量的极限。此时,一个正在运行中的线程调用GetQueuedCompletionStatus时,它就会立刻从队列中取走该完成包。这样就不存在着环境的切换,因为该处于运行中的线程就会连续不断地从队列中取走完成包,而其他的线程就不能运行了。
     完成端口的线程并发量的建议值就是你系统CPU的数目。在这里,要区分清楚的是,完成端口的线程并发量与你为完成端口创建的工作者线程数是没有任何关系的,工作者线程数的数目,完全取决于你的整个应用的设计(当然这个不宜过大,否则失去了IOCP的本意:))。
第三、IOCP开发过程中的消息乱序问题。
     使用IOCP开发的问题在于它的复杂。我们都知道,在使用TCP时,TCP协议本身保证了消息传递的次序性,这大大降低了上层应用的复杂性。但是当使用IOCP时,问题就不再那么简单。如下例:
  
       三个线程同时从IOCP中读取Msg1, Msg2,与Msg3。由于TCP本身消息传递的有序性,所以,在IOCP队列内,Msg1-Msg2-Msg3保证了有序性。三个线程分别从IOCP中取出Msg1,Msg2与Msg3,然后三个线程都会将各自取到的消息投递到逻辑层处理。在逻辑处理层的实现,我们不应该假定Msg1-Msg2-Msg3顺序,原因其实很简单,在Time 1~Time 2的时间段内,三个线程被操作系统调度的先后次序是不确定的,所以在到达逻辑处理层,
Msg1,Msg2与Msg3的次序也就是不确定的。所以,逻辑处理层的实现,必须考虑消息乱序的情况,必须考虑多线程环境下的程序实现。
        在这里,我把消息乱序的问题单列了出来。其实在IOCP的开发过程中,相比于同步的方式,应该还有其它更多的难题需要解决,这也是与Select方式相比,IOCP的缺点,实现复杂度高。
结束语:
    ACE的Proactor Framework,对windows平台的IOCP做了基于Proactor设计模式的,面向对象的封装,这在一定程度上简化了应用开发的难度,是一个很好的异步IO的开发框架,推荐学习使用。 
Reference:
    Microsoft Technet,Inside I/O Completion Ports


http://chatgpt.dhexx.cn/article/gL5IhMfL.shtml

相关文章

异步通信之IOCP详解

一、 概述 学习完网络基础,在写C/S应用程序时,大多童靴写服务器基本都没有用到io模型,基本都是采用“accept同步拥塞通讯和多线程方式”与客户端通讯。但当有成千上万客户端请求连接并与服务器通讯时,多线程的创建与CPU上下文的切…

IOCP详解

IOCP详解 IOCP(I/O Completion Port,I/O完成端口)是性能最好的一种I/O模型。它是应用程序使用线程池处理异步I/O请求的一种机制。在处理多个并发的异步I/O请求时,以往的模型都是在接收请求是创建一个线程来应答请求。这样就有很多…

IOCP技术详解

这几周我接触了Windows网络通讯中的IOCP模型,自己在网上找了相关的知识进行学习,自己又下了好多服务器端的代码,但都运行不了,也是自己菜,能力还需加强。幸好我师父资助了我一个能运行的服务端IOCP代码,自己参照网上的…

Python正则表达式模式

在 Python 程序中,模式字符串使用如下特殊的语法来表示一个正则表达式: 字母和数字表示它们自身,一个正则表达式模式中的字母和数字匹配同样的字符串;当大多数字母和数字前加一个反斜杠时,它们会拥有不同的含义&#…

Python正则表达式实例详解

一、正则表达式语法 正则表达式是用匹配或者描述字符串的工具。 用处: a.判断字符串是否满足某个条件—判断输入的字符串是否是邮箱/手机号码。是否是ip地址 b.提取满足条件的字符串 c.字符串替换 Python中通过re模块中相应的方法来支持正则表达式的匹配、查找和替…

Python正则表达式语法快速入门

文章目录 1 正则符号初阶代码举例1:不同符号的组合代码举例2:符号加,代表连续的一个或多个代码举例3:匹配字符串开始代码举例4:匹配字符串结束代码举例5:匹配单词边界 2 正则符号进阶代码举例1:…

python正则表达式入门

🙊今天我们来学习python的正则表达式的部分,先说下为什么要学习这一部分呢,当然是因为正则表达式处理文本类型的数据实在是太方便了。为以后进入nlp领域打打基础! 先给大家推荐一个网站: 用于正则表达式验证. 大致就长这个样子。…

python使用正则表达式

一、使用正则表达式步骤 1、寻找规律; 2、使用正则符号表示规律; 3、提取信息,如果每一个字符都能匹配,则匹配成功;一旦有匹配不成功的字符则匹配失败。 二、正则表达式中常见的基本符号 1.点号“.”一…

Python 正则表达式

1.正则表达式的定义: 正则表达式是对字符串进行解析(获取一大串字符串信息中,你想要的部分)。 正则表达式是一种文本模式,模式描述在搜索文本时要匹配的一个或多个字符串。 正则表达式,又成正规表示式,正规…

python正则表达式详解

正则表达式是一个很强大的字符串处理工具,几乎任何关于字符串的操作都可以使用正则表达式来完成,作为一个爬虫工作者,每天和字符串打交道,正则表达式更是不可或缺的技能,正则表达式的在不同的语言中使用方式可能不一样…

详解Python正则表达式(含丰富案例)

前言:正则表达式在网络爬虫、数据分析中有着广泛使用,掌握正则表达式能够达到事半功倍的效果。本文详细介绍正则表达式中各种规则及其符号含义,并结合Python中的Re库进行演示,由浅入深,即学即练即用,内容丰…

Python超详细的正则表达式

目录 介绍 常用的正则表达式匹配规则表格 1.match() .group() 常用知识点 1.通用匹配 2.贪婪与非贪婪 3.修饰符 4.转义匹配 2.search() 3.findall() 4.sub() 总结 Hello大家好我是,PYmili!今天给大家带来正则表达式的使用教程即匹配规则表格…

Python正则表达式(一看就懂)

目录 哈喽O(∩_∩)O😄 什么是正则表达式(⊙_⊙) 简单说,正则表达式是… 正则表达式怎么用❓ sreach的用法🍊 匹配连续的多个数值🍉 字符""重复前面一个匹配字符一次或者多次🍉 字符"*"重复前…

Python正则表达式详解 (超详细,看完必会!)

正则表达式详解 正则表达式 英文名称叫 Regular Expression简称RegEx,是用来匹配字符的一种工具,它常被用在网页爬虫,文稿整理,数据筛选等方面,最常用的就是用在网页爬虫,数据抓取。 一、正则表达式的各种…

Python 正则表达式详解(建议收藏!)

目录 match 匹配字符串 单字符匹配 . 匹配任意一个字符 \d 匹配数字 \D 匹配非数字 \s 匹配特殊字符,如空白,空格,tab等 \S 匹配非空白 \w 匹配单词、字符,如大小写字母,数字,_ 下划线 \W 匹配非…

Android工程中方法数超过65536解决方法(Kotlin)

Android Studio报错: The number of method references in a .dex file cannot exceed 64K. Caused by: com.android.tools.r8.utils.AbortException: Error: Cannot fit requested classes in a single dex file (# methods: 68815 > 65536) 解决方案 build.…

postman/EOLINKER测试报错 RangeError:Port should be 0 and 65536. Received 80892.

postman/EOLINKER测试报错 RangeError&#xff1a;Port should be > 0 and < 65536. Received 80892. RangeError&#xff1a;范围错误 RangeError是当一个只超出有效范围时发生的错误。主要的有几种情况&#xff0c;第一是数组长度为负数&#xff0c;第二是Number对象…

Android Studio Cannot fit requested classes in a single dex file (# methods: 72633 > 65536)解决办法

今天在Android Studio中构建Android工程时&#xff0c;出现了这样报错&#xff1a;“Cannot fit requested classes in a single dex file (# methods: 72633 &#xff1e; 65536)” 在网上找了一圈儿&#xff0c;总结个简单快速的解决方法 问题&#xff1a; Cannot fit req…

计算机端口号65536,65536端口能不能用

楼主asdd3000(asdd) 16位计算机端口65536个,32位计算机端口是65536/2个还是65536*2个?为什么? 问题点数:10、回复次数:20 Top 1 楼EvilOctal(冰血封情) 回复于 2004-04-08 20:41:04 得分 0 这个问题我真的不太清楚. 但是 好象和位数没关系吧? 服务端口是国际组织规定的,不是…

为什么89C51单片机里面有TH0=(65536-50000)/256;TL0=(65536-50000)%256;

由于89C51的晶振频率为12MHZ&#xff0c;机器周期是1us,则每1us产生一次计数&#xff0c;例如需要进行50ms的计时&#xff0c;对机器周期进行50000计数就可以得到&#xff0c;T0可以对机器周期进行65536次计数&#xff0c;为了不让T0溢出&#xff0c;我们需要对定时器/计数器赋…