关于IOCP的探讨

本文主要探讨一下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的缺点,实现复杂度高。

若同时投递了三次WSARecv,数据有序到的到达完成队列MSG1,MSG2,MSG3,

三个线程A,B,C同时取向MSG1,MSG2,MSG3

你不能保证A,B,C三个线程,会按顺序完成,

这样就会引起乱序问题!!

============================================

总结:

IOCP之所以产生乱序是因为投递了多个未决的WSARecv,这些WSARecv真实收到数据时候,没有标记哪一个先投递的(先投递先收到数据),从而导致了接收数据包乱序,有效的解决方式就是给投递的WSARecv结构体里面添加一个序列号,以此判断包的顺序。

============================================

关于IOCP乱序的探讨相关推荐

  1. Flink Checkpoint 机制:如何保证 barrier 和数据之间不乱序?

    Flink Checkpoint 机制:如何保证 barrier 和数据之间不乱序? 1 前言 1.1 什么是 state? 要说 checkpoint,首先要从 state 聊起.之前有被问到对于 ...

  2. 简单理解计算机内存乱序

    作者 | 后端技术小牛说   责编 | 张文 头图 | CSDN 下载自视觉中国 本文探讨了自己对内存一致性模型的理解,由于不可避免的需要和操作系统底层打交道,本文主要例子和代码是 C++ 和汇编语言 ...

  3. Apache IoTDB 相关论文《乱序时间序列数据的写入管理》入选国际数据库顶级会议 ICDE 2022

    2022年5月9日,国际数据库顶级会议 ICDE 2022(线上会议)盛大召开.康愈圆同学的< Separation or Not: On Handing Out-of-Order Time-S ...

  4. 在一个文件中有 10G 个整数,乱序排列,要求找出中位数。内存限制为 2G。 -- Shirley对比编程珠玑 inShirley

    http://www.cppblog.com/richbirdandy/archive/2008/09/26/61426.html 题目和基本思路都来源网上,本人加以整理. 题目:在一个文件中有 10 ...

  5. wireshark和tcpdump抓包TCP乱序和重传怎么办?PCAP TCP排序工具分享

    点击上方↑↑↑蓝字[协议分析与还原]关注我们 " 介绍TCP排序方法,分享一个Windows版的TCP排序工具." 在分析协议的过程中,不可避免地需要抓包. 无论抓包条件如何优越, ...

  6. 基于三维向量对的乱序堆叠物体的位姿识别

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 摘要:针对乱序堆叠物体识别效率低.速度慢的问题,提出一种快速可靠的3D对象检测可以应用于复杂场景中随机 ...

  7. python使用random模块生成随机数、实现随机乱序和随机抽样?

    如何使用random模块生成随机数.实现随机乱序和随机抽样? random.random()函数可以生成[0.0, 1.0)之间的随机浮点数. random.uniform(a, b)函数可以生成[a ...

  8. 查处的数据如何乱序_老司机总结常用镜像方法,让镜像数据更加可靠

    在做数据包分析的时候,经常会说一句话,"Packets never lie(数据包不会说谎)",即数据包会真实反应网络服务和应用服务的真实状态.基于网络镜像的流量分析同样如此,如果 ...

  9. TCP数据流稳定性--TCP分片,重组及乱序

    1.IP分片的情况.IP软件包有一个[分片]和[重组]模块,一个IP数据报在传输中可以被ip软件包的[分片]模块分片,在目的接收端B的IP软件包 的[重组]模块重新组合.接收端B的IP软件包如果收到乱 ...

最新文章

  1. 强烈推荐Oracle的入门心得
  2. PHP-7.1 源代码学习:虚拟机字节码
  3. C# SHA1散列算法
  4. JavaScript判断浏览器类型及版本(新增谷歌的Chrome)
  5. Remove Duplicates from Sorted Array II leetcode java
  6. windows下编译c语言文件路径,解决JNI在Windows环境下因长路径导致编译失败问题
  7. 语言const的生命周期_如何理解一门编程语言2——以复制构造函数为例
  8. 日志系统实战(一)—AOP静态注入
  9. 服务器自动访问iis,设置IIS服务器定时自动重启的方法以Windows Server 2008为例
  10. 纬地道路纵断面设计教程_如何高效算量?市政道路从识图算量到施工工艺,一篇全搞定!...
  11. 《初等数论》:整除性概念及其性质、质数与合数
  12. Lattice LSTM
  13. 人像优化效果Lr预设
  14. IE被劫持的手动解除
  15. <JVM笔记:内存与垃圾回收>13-垃圾回收器
  16. jQuery与JavaScript 页面加载事件
  17. 2022主流Nivida显卡深度学习/强化学习/AI算力汇总
  18. mysql excel 同步数据_mysql导入excel数据
  19. 逆战玩着玩着服务器未响应,逆战进不去一直加载,为什么玩逆战加载很久,逆战内置qt修复,逆战进入游戏加载慢,逆战卡加载:逆战没qt了【为什么我逆战进不去QT官方语音】-南开游戏网...
  20. 安装oracle 10g XE后的提示

热门文章

  1. (常用API)正则表达式切割练习
  2. 企业微服务:实现crud操作之创建实体类
  3. 元素的样式设置 元素类样式的操作 开关灯效果 获取兄弟元素 当前元素的兄弟元素样式
  4. mysql设置字段为空字符串_mysql数据类型字段插入空字符串自动填充为0报错
  5. 解决 Maven 报错 Non-resolvable parent POM ... was cached in the local repository, resolution will not be
  6. SpringBoot项目使用微服务后在Service窗口启动应用后不显示端口号
  7. dsPIC30F 细节点问题不定期更新ing
  8. Python——如何搭建Python的环境
  9. 微信公众号文章中图片加载时,占位图宽高大小的确定
  10. android 官方教程中文版