这是对
http://www.iteye.com/topic/102007的一个回帖,颇受好评,得了五星,加了若干分,故收入博客。
基本没什么大错误,经iunknown指正,对non-blocking IO的理解有误。等闲一点,好好翻翻书再改正吧。
--------------------------------
我来侃一侃我对I/O Completion Ports的理解吧。这是一个比较复杂的话题,下面的描述尽量详细,争取把来龙去脉讲清楚,勿嫌啰嗦。
首先讨论一下I/O Completion Ports试图解决什么样的问题。
写一个IO Intensive服务器程序,对每一个客户请求生成一个新的child process/worker thread来处理,每个process/thread使用同步IO,这是最经典古老的解法了。在这之上的改进是prefork 多个process 或者使用线程池。(使用process或thread,原理都差不多,thread的context switch花销要比process switch要小。为了论述简单,下面只讨论线程。)
这种结构的并发性并不高,哪怕你用C++, C甚至汇编来写,效率都不会很高,究其原因,在于两点:
一.同步IO,每个线程大多数时间在等IO request的结束。IO相对于CPU,那是极极慢的。我翻了翻手里的Computer Architecture, A Quantitative Approach第二版,1996年出的,里面对CPU Register, CPU Cache, RAM, Disk,列的access time如下:
Registers: 2-5 nano seconds
CPU Cache: 3-10 nano seconds
RAM: 80-400 nano seconds
Disk: 5 000 000 nano seconds (5 milli seconds)
如今CPU又按照摩尔定律发展了十年后,这个硬盘还是机械式的磁头移来移去读写,尽管如今disk controller都有cache,也在发展,但和CPU相比,差距越来越大。(谁有最新数据可以贴上来。)
二.生成数量大大超过CPU总数的线程。这样做有两个弊端,第一是每个线程要占用内存,Windows底下每个thread自己stack的省缺大小为1M,32位程序下一个用户程序最大能利用的内存也就3G,生成3000个线程,内存就没了。当然有人说64位下面,可以随便浪费,那么,第二个弊端,就无法避免了 ─ 生成大量的线程,CPU必然会花费大量的cpu cycles在线程之间进行切换。如今市场上价格适中的服务器也就2 cpu x 4 core = 8 核而已。生成那么多的线程,CPU在切换线程上花的功夫可能比干正经事还要多。
明白了原因,就可以寻找改进方法。首先,使用异步IO。现在所有主流OS,都提供异步IO(non-blocking IO),连Java这种跨平台的编程环境都在版本1.4里开始支持异步IO了。但是,光有异步IO,这是不够的。论坛里有人发贴子问过,“我的线程发个IO Request,异步IO,直接返回了,然后我的线程干什么?” 异步IO是操作系统提供的机制,我们还需要设计我们程序的结构,使异步IO和线程结合起来,可以充分利用异步IO带来的好处,同时必须控制同时运行线程的数量,减少thread context switch的开销。
IO Completion Port, 是微软针对上述思想,在Windows内核级别,提供的解决方案。
从抽象高度去理解IO Completion Port,可以把它想成一个magic port,一边有一个队列是IO驱动程序处理好的IO数据,另一边是一个小小的线程池,这个port把io数据交给线程池里的线程来处理。同时,别的线程启动了IO异步请求后通知这个port一声,“嘿,注意了,一会儿这个IO handle 会有个数据包传过来要处理。” 这个port回答,“好,我注意一下这个handle。”。
下面我们具体看一下Io Completion Port这个内核对象以及使用。
要创建IoCompletionPort,呼叫Win32函数CreateIoCompletionPort。这个函数一身两用,创建IoCompletionPort也是它,往建好的IoCompletionPort里面加device handle也是它。
HANDLE CreateIoCompletionPort(
HANDLE hfile,
HANDLE hExistingCompPort,
ULONG_PTR CompKey,
DWORD dwNumberOfConcurrentThreads);
// 创建IoCompletionPort
HANDLE hCp;
hCp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 4);
创建IoCompletionPort头三个参数都是NULL之类的,只有第四个参数,用来配置这个生成的IoCompletionPort所允许同时运行的最大线程数目。
创建好的IoCompletionPort kernel object,拥有两个队列。一个是Device List,包含所有通过这个IoCompletionPort管理的异步IO请求 的Device Handle。另外一个是I/O Completion Queue (FIFO),Device Handle对应的IO驱动程序处理好的IO数据,放在这个队列里。
为了能在Device List这个队列里面加个entry,用户程序将再一次使用CreateIoCompletionPort 这个函数。
CreateIoCompletionPort(myHandle, hCp, myKey, 0);
第一个参数是个IO Handle(Windows不限制handle类型,File, Directory, Serial Port, Parallel port, Mailslot server, Mailslot client, pipe, socket等等都可以),第二个参数是以前创建的IoCompletionPort handle. 这个IO Handle将放到IoCompletionPort handle的Device List那个队列里去。第三个参数是个long整数,是用来identify程序Request Context的。因为现在程序不是一个线程来处理客户Request了,而是不同的线程来处理。在一个线程里按顺序一二三四五来实现程序逻辑的方式是不行了,因此作为程序员你要把逻辑的Context记下来,让不同的线程得到这个Context,根据当前的状态,来执行相关的代码。这个completion key,是找到相映的context的key, index, hash code,pointer, whatever.
第二个队列,IO Completion Queue,是由OS往里面插入entry的。OS在处理好了IO异步请求之后,察看一下这个Device handle是否是放在某个Completion Port里面,如果是,OS就在Completion Port的Completion Queue里面加个Entry。这个Entry包括下列数据。
1.Number of bytes transferred
2.Completion key
3.Pointer to I/O request’s OVERLAPPED structure
4.Error code
下面来看看IO Completion Port是怎么管理线程的。
前面说Completion Port有个线程池,这种说法并不是很贴切。Completion Port本身并不创建线程,而只是掌管三个thread队列:
1.Inactive threads waits IO Completion Port
2.Active running threads
3.Threads paused by other reasons, like waiting for something else (i.e. calls WaitForSingleObject, or even stupid but valid, calls Sleep).
线程由程序创建,然后加入第一个队列(waits on IO Completion Port)。为了加入这个队列线程要呼叫一个函数,GetQueuedCompletionStatus。
BOOL GetQueuedCompletionStatus(
HANDLE hCompPort,
PDWORD pdwNumBytes,
PULONG_PTR CompKey,
OVERLAPPED** ppOverlapped,
DWORD dwMilliseconds);
第一个参数是handle to Completion Port,线程通知OS本线程要加入这个Completion Port的第一个队列。这个函数会block当前线程,使其处于inactive状态。
现在再去看看图二的I/O Completion Queue,OS在一份IO异步请求处理好了后,会在这里插入个entry,Completion Port在收到entry后,看看线程池里面有没有空闲没事做的线程,如果有,不要忘记我们创建这个Completion Port时候规定了个最大同时运行线程数量,如果当前运行线程数量小于这个最大值,那么就把这个线程放到第二个(active running)的队列上去,让这个线程运行起来。前面不是说线程在GetQueuedCompletionStatus上面block了么,现在这个函数返回了,继续运行程序的代码。通过这个最大同时运行线程数量,保证了不会有太多的线程在运行,Viola! 本文开头分析的几个问题全解决了。即是异步IO,又把异步IO和线程池结合了起来,还控制了当前运行线程数量。It’s BEAUTIFUL!
这个线程处理完程序逻辑后,呼叫一下GetQueuedCompletionStatus,又回到了第一个队列。有意思的是这个队列的逻辑是Last In First Out。如果又有IO数据等待线程处理,这个线程可以继续执行,不用进行Context Switch,典型的能者多劳啊,越能干的人干的越多。
这个线程在处理程序逻辑的过程中,可能会因为别的原因而变成inactive,比如在等别的资源(WaitForSingleObject),或者变态一点,自己来了个Sleep,这时线程就给放到第三个队列去了。
这里有个有趣的现象,假如开始我们在第一个队列里面放三个线程,而最大同时运行线程数量设为2,在两个线程跑起来之后,第三个就不跑了,如果这时运行中的某个线程因为等别的资源而变为inactive,那么第三个线程也开始跑起来,同时运行线程数量还是2,这时那个等别的资源的线程等到资源了,又开始跑了起来,这时同时运行线程数量就是3,比设定的2要大。
推荐的最大同时运行线程数量一般为CPU的总数,但是如果运行的线程还要等别的资源,建议把这个数目稍微设大一点,这样并发率会更高。
先写这么多吧,累死了。这个帖子讲述的是IO Completion Port的工作原理,还没有谈到它是如何实现的,也还没有扯到Windows Source Code. 如果对工作原理了解了,也未必要去深究是如何实现的。在没有IO Completion Port的系统上,自己实现一个,也未必不可。当然讨论讨论是如何实现的,也是件很有趣的事情。这方面我也没有完全搞个水落石出。让我先攒攒劲,改天再写吧。
另外搭个顺风车问个问题,我做图的水平很臭,手里就有个Visio还用不好。本文的图都是在Word里面画的。请问能否推荐个好用的作图软件,免费还是花钱的都可以。要有什么画UML比较好的软件,也推荐推荐,Visio太不好用了。
分享到:
相关推荐
标题 "A simple application using I/O Completion Ports and WinSock" 提及了两个关键概念:I/O 完成端口(I/O Completion Ports, IOCP)和 WinSock。WinSock 是 Windows 操作系统中的套接字库,用于实现网络通信。...
【Client_Completion】,也称为I/O完成端口(I/O Completion Ports,简称IOCP),是Windows操作系统中一种高效的多线程I/O处理机制。它主要用于优化大量的并发I/O操作,如网络通信、磁盘读写等。在高并发场景下,...
6. **完成端口(I/O Completion Ports, IOCP)** 完成端口是Windows特有的高级异步I/O机制,它可以高效地处理大量并发连接。IOCP通过`CreateIoCompletionPort`、`GetQueuedCompletionStatus`和`...
“I/O完成端口模型”(I/O Completion Ports, 简称IOCP)是一种高效的Windows系统中的多线程I/O管理机制,它被广泛应用于网络通信类应用,特别是那些需要处理大量并发连接的服务,如服务器端程序。在TCP/IP协议栈中...
Windows NT contains features such as I/O completion ports that help boost network server performance and scalability. In this paper we focus on improving the Linux implementation of poll() to reduce ...
### Windows Socket五种I/O模型详解 在计算机网络编程中,Windows Socket(Winsock)是实现网络通信的重要机制之一。为了高效地处理网络数据传输,Winsock提供了多种输入/输出(I/O)模型来满足不同场景下的需求。...
在本文中,我们将深入探讨异步Socket I/O模型的几种常见实现方式,包括选择(select)、异步选择(asynchronous select)、事件选择(poll)、重叠I/O(overlapped I/O)以及完成端口(completion ports)。...
【标题】"IOCp完成端口的代理服务器源代码"涉及到的是Windows系统中网络编程的一个高级技术,即I/O Completion Ports (IOCp)。在Windows操作系统中,IOCp是一种高效处理大量并发I/O操作的机制,特别适用于构建高性能...
在Windows平台上,I/O Completion Port 是实现基于I/O的多线程模型的核心组件之一。它允许应用程序将I/O操作注册到特定的Completion Port,并通过异步I/O完成通知来处理这些操作。 ##### 3.1 创建Completion Port ...
完成端口(IO Completion Ports, I/OCP)是Windows操作系统中一种高效的I/O模型,尤其适用于高并发的网络服务,如代理服务器。本文将详细解释完成端口的工作原理,并通过描述提供的“采用完成端口的代理服务器原型...
重叠IO模型属于异步I/O的一种,但与标准的异步I/O(如POSIX的aio_*函数)不同,它在Windows操作系统中被广泛使用,被称为完成端口(I/O Completion Ports,IOCP)。 重叠IO模型的工作流程如下: 1. **初始化**:...
### 当前Windows支持的各种Socket I/O模型 在现代操作系统中,网络编程是不可或缺的一部分,而Socket I/O模型则是实现高效网络通信的关键技术之一。本文将详细介绍当前Windows系统中支持的各种Socket I/O模型,包括...
【描述】:Windows I/O完成端口(I/O Completion Ports,简称IOCP)是Windows操作系统提供的一种高效、多线程的I/O模型,它允许应用程序处理大量的并发I/O操作。IOCP在高并发环境下尤其有用,如网络服务器、数据库...
"IOSArchive"这个主题就是关于如何在iOS应用中实现解压缩功能。本文将深入探讨这一技术,结合描述中的博客教程,我们将讲解如何在iOS环境下进行解压缩操作。 首先,iOS系统并未内置直接解压缩ZIP、RAR等格式文件的...
2. **Windows的四种异步I/O模型**:这包括完成端口(Completion Ports)、异步过程调用(Asynchronous Procedure Calls, APCs)、I/O完成端口(I/O Completion Ports, IOCPs)以及使用Overlapped I/O(重叠I/O)和...
IOCP(I/O Completion Port,I/O完成端口)是Windows操作系统提供的一种高效的异步I/O模型,尤其适用于处理大量并发I/O操作。在这个场景中,"MFC IOCP模型异步IO"指的是使用MFC来实现基于IOCP的异步I/O操作。 IOCP...
而在Windows平台,I/O Completion Ports(IOCP)被认为是一种高效的设计,通过Inside I/O Completion Ports可以深入了解其工作原理。 Libevent和Libuv是两个流行的事件库,它们提供了抽象层以简化跨平台的异步I/O...
完成端口(IOCP,Input/Output Completion Port)是Windows操作系统提供的一种高效的多线程并发I/O模型,尤其适用于网络编程中的高并发场景。在IOCPDemo中,我们将会探讨如何利用这种模型来构建高性能的UDP多播...