windows IOCP 实践
关于 windows IOCP
有人说 windows IOCP 是 windows 上最好的东西。 IOCP 是真正的异步 IO,意味着每次发起一个 IO 请求,该调用本身则立即返回, 而包括 IO 操作和数据从内核缓冲区到用户缓冲区之间的拷贝都由系统完成,直到这个过程结束系统才通知用户进程。 linux 上没有这样的异步 IO。
IOCP 的使用
- 创建一个新的完成端口。完成端口被设计成与一个线程池相互合作,线程池的线程并发的用来处理完成的 IO 通知。
CreateIoCompletionPort
这个 API 用于创建 IOCP, 最后一个参数则是指定线程池中线程个数,一般来说取 CPU * 2 ,这样可以最充分使用多核 CPU ,又降低了线程间的切换。CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, dwNumberOfConcurrentThreads)
- 创建工作线程。
- 关联一个 IO 设备到完成端口。也是调用
CreateIoCompletionPort
(API 设计有些太随意了吧,难道有什么历史原因?)。HANDLE h = CreateIoCompletionPort(hDevice, hCompletionPort, dwCompletionKey, 0);
- 使用 overlapped IO ,例如 socket 的 WSARecv/WSASend,甚至 AcceptEx 和 ConnectEx。这些调用都只是发起一个 IO 请求然后立即返回。函数调用都需要初始化一个 OVERLAPPED 结构体,后面有提到其作用。
- 工作线程是一个 loop, 阻塞在
GetQueuedCompletionStatus
调用上。GetQueuedCompletionStatus
返回时从 IO 完成队列中取出一个 completion packet。线程池线程阻塞时是由系统负责完成调度的。
IOCP 内部的一些数据结构
- Device List:包含所有与完成端口相关联的设备的一个列表。
- I/O Completion Queue(FIFO):当一个异步 IO 请求完成了,系统会去检查是否这个 IO 设备与任何 IO 完成端口关联了,如果是,系统会在 IO 完成端口队列的末尾添加一个 completion packet(以 FIFO 的顺序入队),
GetQueuedCompletionStatus
就是在这个队列上等待。 - IOCP 关联线程等待队列:线程池中的线程调用
GetQueuedCompletionStatus
时,就会被放进一个等待队列,IO 完成端口内核对象根据此队列知道有哪些线程在等待处理completion packet。线程等待队列是按照 LIFO 的方式入队的,也就是当有一个 completion packet 到来时,系统先唤醒最后调用GetQueuedCompletionStatus
进入等待队列的线程。
IOCP 和线程池的相互作用
- 任何线程都可以调用
GetQueuedCompletionStatus
来与一个 IO 完成端口关联起来,但是一个线程只能关联一个 IOCP,当线程退出或者指定了其他的 IOCP或者关闭了 IOCP,线程才与这个 IOCP 解开绑定。 - 创建 IOCP 的时候会指定一个并发值,虽然任意个线程可以关联到这个 IOCP,但是并发值限定了可以同时运行的线程数。假设这样的场景,有一个并发值为 1 的 IOCP,但是有多于一个的线程关联到了这个 IOCP,如果完成队列中总是有一个 completion packet 在等待,当正在运行的线程调用
GetQueuedCompletionStatus
时就会立即返回,该线程处理完这个 completion packet 再次调用GetQueuedCompletionStatus
又会立即返回。在处理 completion packet过程中,虽然完成队列中始终有 completion packet 待处理,但是因为并发值为 1 的原因,系统不会去调度其他线程来执行,尽管关联 IOCP 的线程不止一个。同时也避免了线程切换的开销,因为始终都是这一个线程在执行。 - 在上述情况中,看起来线程池中关联的其他线程毫无用处,但是其实是没有考虑到正在运行的线程进入等待状态或者因为某种情况与该 IOCP 解除绑定时的情况。如果正在运行的线程调用
Sleep
,WaitFor*
,或者一个同步 IO 函数,或者任何可以引起当前线程从运行状态变为等待状态的函数时,IOCP 就会立即调度其他关联的线程,维持始终有一个线程在运行。
IOCP 使用过程中遇到的问题
- 因为涉及到多线程会比 epoll + 单线程要编码复杂。
- API 设计比较糟糕,这也加大了编码难度。
- 文档描述不清晰,甚至没有一个官方的示例程序,非官方文档或者程序或多或少有些错误,让人难以放心使用。以 WSARecv 为例子,MSDN上描述若 WSARecv 能立即返回,返回值为 0。这是不是意味着程序要在两处处理 IO 完成的情况,一处是 IO 立即返回时,一处是工作线程
GetQueuedCompletionStatus
等待 IOCP 完成队列处。几乎所有的异步 IO 函数都是如此。但是所幸似乎即使立即返回 0 ,完成队列中也会有一个 completion packet,所以只在工作线程中的完成队列中等待 IO 完成也不会出错。 - 一般的使用 TCP 进行通讯的网络程序,因为 TCP 流无界的特性,都会自定义成这样的应用网络数据包:前面几个字节代表该包的长度,后面就是该包真正的内容。应用程序在解包的时候,对应的要先获取包的长度,再截取对应长度的包数据。这样的过程在多线程的 IOCP 会比较困难,多个线程取到了各个数据包的不同部分,而且因为 completion packet 的出队顺序并不能保证,各个线程获取的数据包之间的顺序已经丢失了。因此,必须想办法解决包的顺序问题,而且解包过程需要同步各个线程。这样无疑使得代码变得更复杂。
- IOCP 作为异步 IO ,可以非常方便的发起 IO ,但是每次发起 IO 时候都必须提交一段用户内存,在 IO 完成之前这段内存必须是被锁住的,既你不能再使用。当然这不是 IOCP 的问题,这是异步 IO特性决定的。
一个收发 TCP 应用协议包的程序示例
- 协议包定义成头两个字节保存包长度 len,包头后面 len 字节是包的具体内容。为了简化编码,又能利用到 IOCP 一些特性,决定只启动一个工作线程处理所有的 IO 完成操作,发包和收包都是非阻塞的异步调用。
- 提交给异步 IO 的 buffer,都是从一段预先分配的内存中取出来的,这样使得IO 操作使用的内存是可控的,并且不会有内存碎片,充分使用内存。
- IOCP 的几个核心 API 都与参数 completionKey, overlapped 有关。在程序中 completionKey 可以对应是对哪个 socket 进行操作,overlapped 则对应成具体哪一个 IO 操作。
- 同一时间只允许一个同类的 IO 操作(读或者写)在提交。
代码在此,服务端程序比较简单,可以自己实现并验证。
windows IOCP 实践的更多相关文章
- Docker for Windows(五)实践搭建SqlServer服务&执行数据库操作
上一篇我们已经搭建了一个mysql数据库服务了:Docker for Windows(四)实践搭建&删除MySQL服务,发现用Docker确实是方便且容易,但上一篇主要是服务的搭建删除等基础操 ...
- windows—IOCP
一.重叠I/O回声服务器端 服务端: #include <stdio.h> #include <stdlib.h> #include <WinSock2.h> #d ...
- 【笨嘴拙舌WINDOWS】实践检验之剪切板查看器【Delphi】
该程序能够监视Windows剪切板的内容(文字和图片) 其思路是 先调用SetClipBoardViewer(Self.Handle),让Windows剪切板内容发生改变之后,通知本程序: 然后截获W ...
- 【笨嘴拙舌WINDOWS】实践检验之按键精灵【Delphi】
通过记录键盘和鼠标位置和输入信息,然后模拟发送,就能够创建一个按键精灵! 主要代码如下: library KeyBoardHook; { Important note about DLL memory ...
- 【笨嘴拙舌WINDOWS】实践检验之屏幕取色
实践是检验真理的唯一标准 要取得屏幕的颜色,首先需要创建一个屏幕DC,然后使用该DC,调用GetPixel就可以了 "Note:GetPixel传入的DC应该是屏幕的DC,而不是桌面的DC, ...
- WSL与Windows交互实践
1. WSL是什么 2. WSL新特性 3. WSL管理配置 4. WSL交互 5. 解决方案 * 5.1 使用别名 * 5.2 多复制一份 * 5.3 重定向 * 5.4 symlink 6 ...
- windows IOCP入门的一些资料
最近需要看一个中间件的IOCP处理模块,需要了解这方面的知识 https://www.codeproject.com/articles/13382/a-simple-application-using ...
- Docker for Windows(四)实践搭建&删除MySQL服务
我们已经下载安装好了Docker for Windows:Docker for Windows(一)下载与安装,也简单了解了Docker常用命令:Docker for Windows(三)Docker ...
- 使用 IIS 在 Windows 上托管 ASP.NET Core(Windows安装实践)
原文地址 https://docs.microsoft.com/zh-cn/aspnet/core/host-and-deploy/iis/?view=aspnetcore-2.0&tabs= ...
随机推荐
- Salesforce LWC学习(三十) lwc superbadge项目实现
本篇参考:https://trailhead.salesforce.com/content/learn/superbadges/superbadge_lwc_specialist 我们做lwc的学习时 ...
- CentOS7下常用安装服务软件yum方式的介绍
简介:介绍yum软件包的管理并配置本地yum源 yum安装:基于 C/S 架构,yum安装称之为傻瓜式安装 yum安装优点:方便快捷,不用考虑包依赖,自动下载软件包. yum安装缺点:人为无法干预,无 ...
- 通过`RestTemplate`上传文件(InputStreamResource详解)
通过RestTemplate上传文件 1.上传文件File 碰到一个需求,在代码中通过HTTP方式做一个验证的请求,请求的参数包含了文件类型.想想其实很简单,直接使用定义好的MultiValueMap ...
- Tensorflow2.0-mnist手写数字识别示例
Tensorflow2.0-mnist手写数字识别示例 读书不觉春已深,一寸光阴一寸金. 简介:通过CNN 卷积神经网络训练后识别出手写图片,测试图片mnist数据集中的0.1.2.4. ...
- Arduino IDE 开发 ESP-01S/ESP-01物联网实战检测温度湿度上传MQTT服务器
一.硬件准备 USB转ESP8266两块.DHT11温度湿度传感器.ESP8266-01/ESP8266-01一块(如果学习的话多买几块,ESP-01/ESP-01S的区别) USB转ESP8266 ...
- 原生小程序中实现将scss文件实时编译为wxss文件
参考链接 全局安装gulp,方便以后直接执行gulp命令 npm install gulp -g 用原生小程序新建一个项目 在小程序根目录(app.js同级目录)中新建package.json文件 n ...
- Node项目模板管理脚手架ptm-cli开发
目录 一.ptm-cli 使用说明 1.特点 2.安装 3.使用 1)基础帮助命令 2)添加模板/项目 3)编辑模板/项目 4)查看模板/项目 5)删除模板/项目 6)基于模板新建/初始化项目 二 p ...
- body-parser 源码分析
body-parser 源码分析 预备知识:熟悉 express 的中间件逻辑 阅读事件:30min 1. body-parser 解决什么问题 在 node http 模块中,您只能通过 data ...
- 第二章 进程同步(二)——> 重点
2.4 进程同步 2.4.1 进程同步的基本概念 1. 两种形式的制约关系 (1)间接相互制约关系:互斥问题(往往是互斥设备)---是同步的特例 (2)直接相互制约关系:同步问题 注: 互斥问题 ...
- 洛谷P4848 崂山白花蛇草水 权值线段树+KDtree
题目描述 神犇 \(Aleph\) 在 \(SDOI\ Round2\) 前立了一个 \(flag\):如果进了省队,就现场直播喝崂山白花蛇草水.凭借着神犇 \(Aleph\) 的实力,他轻松地进了山 ...