IOCP完成端口详解(10年吐血大总结)

合集下载

名词解释IOCP简介四-Read

名词解释IOCP简介四-Read

hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
该语句的作用是返回一个句柄,在为完成端口分配了一个套接字句柄后,用来对那个 端口进行标定(引用)。
函数功能:注意该函数实际用于两个明显有别的目的: 1. 用于创建一个完成端口对象。 2. 将一个句柄同完成端口关联到一起。
IOCP简介

IOCP的开发
1、CreateIoCompletionPort
最开始创建一个完成端口时,唯一感兴趣的参数便是 NumberOfConcurrentThreads(并发线程的数量);前面三个参数都会被忽略。 NumberOfConcurrentThreads参数的特殊之处在于,它定义了在一个完成端口上, 同时允许执行的线程数量。理想情况下,我们希望每个处理器各自负责一个线程的运 行,为完成端口提供服务,避免过于频繁的线程“场景”切换。若将该参数设为0Байду номын сангаас 表明系统内安装了多少个处理器,便允许同时运行多少个线程!可用下述代码创建一 个I/O完成端口:
IOCP简介

IOCP的应用
可以看出完成端口是到目前为止最为复杂的输入输出模式。然而,当一个 应用不得不同时处理大量的socket时,它也提供了使系统性能达到最佳的可 能性。只有在被迫面对几百甚至几千个并发的socket、你又希望在添加CPU 后可以获得更好的scale时,才被派上战场。关于完成端口,最重要的是记住 这一点:如果你为winnt/2000开发处理大量socket I/O 请求的高性能服 务,它是你的最佳选择 IOCP不仅仅在通信socket上,同时也可以用于其他方面,例如读写文件, 比如把文件句柄关联到完成端口上,产生一定量的工作器线程,读取文件不同 的部分实际读取数据是系统内部处理,只是读取完了通知一下,并且把相关I O数据填充到结构体中

IOCP模型总结

IOCP模型总结

IOCP模型总结IOCP模型基于Windows操作系统的异步I/O机制,利用操作系统提供的I/O完成端口来管理I/O操作。

在IOCP模型中,主线程将I/O操作的控制权交给I/O线程池去完成,从而提高了系统的并发处理能力。

当I/O操作完成后,操作系统会通知应用程序将已经完成的I/O操作从I/O 完成端口中取出并进行处理。

1.高性能:IOCP模型使用异步I/O的方式,避免了传统的同步I/O 中频繁的等待和轮询操作,从而减少了CPU的资源消耗。

2.可扩展性:IOCP模型利用了线程池来管理I/O操作,通过配置线程池的线程个数可以调整系统的扩展性,适应高负载的场景。

3.可靠性:IOCP模型在设计上考虑了请求处理的完整性,异步I/O 操作与应用程序的逻辑分离,保证了I/O操作的可靠性。

4. 多协议支持:IOCP模型不仅支持TCP和UDP协议,还支持其他的网络协议,如IPX/SPX、NetBEUI等。

1.高并发处理能力:IOCP模型通过使用异步I/O和I/O线程池,可以高效地处理大量的并发请求,提高了系统的并发处理能力。

2.低系统开销:IOCP模型避免了传统同步I/O模型中的频繁的等待和轮询操作,减少了系统开销,提高了系统的性能。

3.灵活的扩展性:IOCP模型使用线程池来管理I/O操作,通过调整线程池的大小可以灵活地扩展系统的能力,适应不同的负载需求。

4.容易实现和使用:IOCP模型提供了简单的API接口,易于实现和使用,不需要过多的底层细节和复杂的编程逻辑。

1.网络服务器:IOCP模型在网络服务器中具有广泛的应用,可以高效地处理大规模的并发网络请求,提高服务器的性能和吞吐量。

2.实时数据处理:IOCP模型适用于需要实时处理大量数据的场景,如实时数据采集、实时广播、实时监控等。

3.高性能计算:IOCP模型在需要高性能计算的场景中也有应用,如科学计算、金融分析、图像处理等。

总结:IOCP模型是一种高效的I/O模型,在Windows系统中具有重要的地位和广泛的应用。

IOCP模型总结

IOCP模型总结

IOCP模型总结
IOCP模型的基本原理是使用操作系统提供的一个输入/输出完成端口,服务器在等待I/O完成时可以继续处理其他的请求(非阻塞),当操作系
统I/O操作完成之后,会通过回调函数的方式通知服务器,从而让服务器
能够及时处理已完成的请求。

2. 创建工作线程(CreateThread):服务器需要创建一定数量的工
作线程,用于处理来自客户端的请求和处理完成端口的通知。

这些工作线
程会在每一个请求到来时进行处理,当有I/O操作完成时,通过回调函数
的方式通知工作线程进行处理。

1.高吞吐量:通过多线程异步I/O的方式,充分利用了硬件性能和操
作系统的特性,能够处理大量的并发请求,提高服务器的吞吐量。

2.高性能:由于保持了非阻塞状态,减少了线程的阻塞时间,服务器
能够更快地响应请求,提供更好的性能。

3.可扩展性好:通过使用多线程模型,服务器可以根据需要动态调整
工作线程的数量,以适应不同的负载情况。

4.高并发处理能力:IOCP模型使用操作系统的通知机制,可以同时
处理多个I/O请求,大大提高了服务器的并发处理能力。

5.方便管理和维护:IOCP模型对服务器的管理和维护提供了便利,
通过将I/O完成的通知传递给操作系统,不需要服务器自己去维护和管理
线程,也无需关心线程的创建和销毁等问题。

总之,IOCP模型是一种高性能、高并发的I/O处理模式,通过利用
操作系统的特性和硬件性能,提高了服务器的处理能力。

它广泛应用于网
络服务器、数据库服务器等需要处理并发请求的场景,能够为用户提供更快速、更稳定的服务。

完成端口讲解

完成端口讲解

完成端口一、什么是完成端口从本质上讲,完成端口是一种异步I/O技术,它提供一个内核对象,可以关联多个I/O设备,同时关联一个线程池,线程池中的线程通常处于睡眠状态,当有I/O出现时,完成端口唤醒等待线程队列中的线程进行处理。

完成端口有着良好的伸缩性灵活性以及较高的效率,一般用来创建大型的服务器。

我们知道,一个服务器应用程序结构可以分为串行模式和并发模式。

在串行模式中,一次只能处理一个请求,第二个请求必须等待第一个请求被处理完毕才能开始处理,适合于客户量比较小的情况;在并发模式中,针对每个请求创建一个线程,使得多个请求可以同时得到处理,因而提高了程序的性能。

但是,我们再进一步思考,如果有多个设备同时发出IO请求,那么在并发模式中也必须创建与之相同个数的线程,但是,CPU的个数是有限的,多于CPU个数的可运行线程就没有意义了,系统不得不在多个线程间进行上下文切换,以使得多个线程并发执行,这必然浪费宝贵的CPU周期。

另外,虽然创建线程较进程而言开销要小,但也并不意味着没有开销,尤其当数量比较大的时候。

在完成端口模型中,引入了线程池的概念,在应用程序初始化时创建一个线程池,在没有请求时处于等待状态,当请求完成时唤醒一个线程运行,运行完毕后重新放入线程池中,等待其他请求使用。

由于不必为每个请求创建一个线程,从而减少了线程的数量,省去了运行中途创建线程的开销,进一步提高了程序的性能。

二、完成端口的内部结构由于完成端口也是一个内核对象,故我们看一下它的内部结构。

完成端口对象包含五个不同的数据结构:1、设备列表:表相:设备句柄、完成键。

当调用CreateIoCompletionPort时将设备与完成端口关联起来,同时在该数据结构中创建一项。

每当向完成端口关联一个设备时,系统向该完成端口的设备列表中加入一条信息。

2、/index.php/Main_Page-->: 150%; mso-bidi-font-size: 10.5pt">I/O完成队列:表相:传输的字节数、32位完成键、I/O请求的OVERLAPPED结构指针、错误代码当一个设备的异步I/O请求完成时,系统检测该设备是否关联了一个完成端口,如果是,系统就向该完成端口的I/O完成队列中加入完成的I/O请求项。

IOCP

IOCP

完成IO使用总结IOCP(I/O Completion Port,I/O完成端口)是性能最好的一种I/O模型。

它是应用程序使用线程池处理异步I/O请求的一种机制。

在处理多个并发的异步I/O请求时,以往的模型都是在接收请求是创建一个线程来应答请求。

这样就有很多的线程并行地运行在系统中。

而这些线程都是可运行的,Windows内核花费大量的时间在进行线程的上下文切换,并没有多少时间花在线程运行上。

再加上创建新线程的开销比较大,所以造成了效率的低下。

调用的步骤如下:抽象出一个完成端口大概的处理流程:1:创建一个完成端口。

2:创建一个线程A。

3:A线程循环调用GetQueuedCompletionStatus()函数来得到IO操作结果,这个函数是个阻塞函数。

4:主线程循环里调用accept等待客户端连接上来。

5:主线程里accept返回新连接建立以后,把这个新的套接字句柄用CreateIoCompletionPort 关联到完成端口,然后发出一个异步的WSASend或者WSARecv调用,因为是异步函数,WSASend/WSARecv会马上返回,实际的发送或者接收数据的操作由WINDOWS系统去做。

6:主线程继续下一次循环,阻塞在accept这里等待客户端连接。

7:WINDOWS系统完成WSASend或者WSArecv的操作,把结果发到完成端口。

8:A线程里的GetQueuedCompletionStatus()马上返回,并从完成端口取得刚完成的WSASend/WSARecv的结果。

9:在A线程里对这些数据进行处理(如果处理过程很耗时,需要新开线程处理),然后接着发出WSASend/WSARecv,并继续下一次循环阻塞在GetQueuedCompletionStatus()这里。

归根到底概括完成端口模型一句话:我们不停地发出异步的WSASend/WSARecv IO操作,具体的IO处理过程由WINDOWS系统完成,WINDOWS系统完成实际的IO处理后,把结果送到完成端口上(如果有多个IO 都完成了,那么就在完成端口那里排成一个队列)。

Java与完成端口IOCP

Java与完成端口IOCP

Java与完成端口IOCPJava与完成端口IOCP传统的Server/Client实现是基于Thread per request,即服务器为每个客户端请求建立一个线程处理,单独负责处理一个客户的请求。

大多数的网络游戏的服务器都会选择非阻塞select这种结构,为什么呢?因为网络游戏的服务器需要处理的连接非常之多,并且大部分会选择在Linux/Unix下运行,那么为每个用户开一个线程实际上是很不划算的,一方面因为在Linux/Unix下的线程是用进程这么一个概念模拟出来的,比较消耗系统资源,另外除了I/O之外,每个线程基本上没有什么多余的需要并行的任务,而且网络游戏是互交性非常强的,所以线程间的同步会成为很麻烦的问题。

由此一来,对于这种含有大量网络连接的单线程服务器,用阻塞显然是不现实的。

iocp,在linux下使用epoll关于线程是这样的,肯定不可能一个用户一个线程的,没见过那么做的,通常我们是这样的,我们创建几个线程分别用于发送和接收网络消息,当然数量也不是太多,通常是CPU个数的2倍,然后另外建立一个逻辑线程,所有的网络线程接收到的数据都会打入这个逻辑线程,以保证逻辑处理中的顺序处理. 不知道你是否理解. 另外一个小提示,QQGAME可不是一个进程就500个连接,通常他一个进程都会达到20000左右的连接数.NIO服务器最核心的一点就是反应器模式:当有感兴趣的事件发生的,就通知对应的事件处理器去处理这个事件,如果没有,则不处理。

所以使用一个线程做轮询就可以了。

当然这里这是个例子,如果要获得更高性能,可以使用少量的线程,一个负责接收请求,其他的负责处理请求,特别是对于多CPU时效率会更高。

JDK 7,WEB服务器 Tomcat、Jetty等,在Windows下,Java将可以使用IOCP,而不是现在nio所用的select,网络并发性能将会得到大幅度提升。

在Linux下则应该改变不多,毕竟linux现在并发最好性能的网络I/O EPOLL,JDK 6.0 nio包含5.0的后续版本的缺省实现就是epoll。

IOCP完全解析

IOCP完全解析

DWORD Flags = 0; // 单 I/O 操作数据 LPPER_IO_DATA PerIoData = NULL; PerIoData = (LPPER_IO_DATA)GlobalAlloc(GPTR, sizeof(PER_IO_DATA)); ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED)); PerIoData->DataBuf.len = 1024; PerIoData->DataBuf.buf = PerIoData->buffer; PerIoData->OperationType = 0; // read WSARecv(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags, &(PerIoData->Overlapped), NULL); } /**/////////////////////////////////////////////////////////////////////////// return nRetCode; } /**/////////////////////////////////////////////////////////////////////////// DWORD WINAPI ServerWorkerThread(LPVOID lpParam) { HANDLE CompletionPort = (HANDLE)lpParam; DWORD BytesTransferred; LPOVERLAPPED lpOverlapped; LPPER_HANDLE_DATA PerHandleData = NULL; LPPER_IO_DATA PerIoData = NULL; DWORD SendBytes; DWORD RecvBytes; DWORD Flags; BOOL bRet = FALSE; while (TRUE) { bRet = GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (PULONG_PTR) &PerHandleData,

完成端口

完成端口

Windows socket之IO完成端口(IOCP)模型开发IO完成端口是一种内核对象。

利用完成端口,套接字应用程序能够管理数百上千个套接字。

应用程序创建完成端口对象后,通过指定一定数量的服务线程,为已经完成的重叠IO操作提供服务。

该模型可以达到最后的系统性能。

完成端口是一种真正意义上的异步模型。

在重叠IO模型中,当Windows socket应用程序在调用WSARecv函数后立即返回,线程继续运行。

另一线程在在完成端口等待操作结果,当系统接收数据完成后,会向完成端口发送通知,然后应用程序对数据进行处理。

为了将Windows打造成一个出色的服务器环境,Microsoft开发出了IO完成端口。

它需要与线程池配合使用。

服务器有两种线程模型:串行和并发模型。

串行模型:单个线程等待客户端请求。

当请求到来时,该线程被唤醒来处理请求。

但是当多个客户端同时向服务器发出请求时,这些请求必须依次被请求。

并发模型:单个线程等待请求到来。

当请求到来时,会创建新线程来处理。

但是随着更多的请求到来必须创建更多的线程。

这会导致系统内核进行上下文切换花费更多的时间。

线程无法即时响应客户请求。

伴随着不断有客户端请求、退出,系统会不断新建和销毁线程,这同样会增加系统开销。

而IO完成端口却可以很好的解决以上问题。

它的目标就是实现高效服务器程序。

与重叠IO相比较重叠IO与IO完成端口模型都是异步模型。

都可以改善程序性能。

但是它们也有以下区别:1:在重叠IO使用事件通知时,WSAWaitForMultipleEvents 只能等待WSA_MAXIMUM_WAIT_EVENTS(64)个事件。

这限制了服务器提供服务的客户端的数量。

2:事件对象、套接字和WSAOVERLAPPED结构必须一一对应关系,如果出现一点疏漏将会导致严重的后果。

完成端口模型实现包括以下步骤:1:创建完成端口2:将套接字与完成端口关联。

3:调用输入输出函数,发起重叠IO操作。

IOCP完成端口原理

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的方式,我就不提了:)):第一、线程池的方式。

使用线程池来对客户端请求进行服务。

使用这种方式时,当客户端对服务器的连接是短连接(所谓的短连接,即:客户端对服务器不是长时间连接)时,是可以考虑的。

完成端口详细解析

完成端口详细解析

关于完成端口(IOCP)的文章汇总- [C/C++]版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明/logs/32007489.html首先讨论一下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如下:Java代码1.Registers: 2-5 nano seconds2.CPU Cache: 3-10 nano seconds3.RAM: 80-400 nano seconds4.Disk: 5000000 nano seconds (5 milli seconds)如今CPU又按照摩尔定律发展了十年后,这个硬盘还是机械式的磁头移来移去读写,尽管如今disk controller都有cache,也在发展,但和CPU相比,差距越来越大。

(谁有最新数据可以贴上来。

)二.生成数量大大超过CPU总数的线程。

这样做有两个弊端,第一是每个线程要占用内存,Windows底下每个thread自己stack的省缺大小为1M,32位程序下一个用户程序最大能利用的内存也就3G,生成3000个线程,内存就没了。

IOCP详解

IOCP详解

IOCP详解简介: IOCP(I/O Completion Port,I/O完成端⼝)是性能最好的⼀种I/O模型。

它是应⽤程序使⽤线程池处理异步I/O请求的⼀种机制。

IOCP详解IOCP(I/O Completion Port,I/O完成端⼝)是性能最好的⼀种I/O模型。

它是应⽤程序使⽤线程池处理异步I/O请求的⼀种机制。

在处理多个并发的异步I/O请求时,以往的模型都是在接收请求是创建⼀个线程来应答请求。

这样就有很多的线程并⾏地运⾏在系统中。

⽽这些线程都是可运⾏的,Windows内核花费⼤量的时间在进⾏线程的上下⽂切换,并没有多少时间花在线程运⾏上。

再加上创建新线程的开销⽐较⼤,所以造成了效率的低下。

Windows Sockets应⽤程序在调⽤WSARecv()函数后⽴即返回,线程继续运⾏。

当系统接收数据完成后,向完成端⼝发送通知包(这个过程对应⽤程序不可见)。

应⽤程序在发起接收数据操作后,在完成端⼝上等待操作结果。

当接收到I/O操作完成的通知后,应⽤程序对数据进⾏处理。

完成端⼝其实就是上⾯两项的联合使⽤基础上进⾏了⼀定的改进。

⼀个完成端⼝其实就是⼀个通知队列,由操作系统把已经完成的重叠I/O请求的通知放⼊其中。

当某项I/O操作⼀旦完成,某个可以对该操作结果进⾏处理的⼯作者线程就会收到⼀则通知。

⽽套接字在被创建后,可以在任何时候与某个完成端⼝进⾏关联。

众所皆知,完成端⼝是在WINDOWS平台下效率最⾼,扩展性最好的IO模型,特别针对于WINSOCK的海量连接时,更能显⽰出其威⼒。

其实建⽴⼀个完成端⼝的服务器也很简单,只要注意⼏个函数,了解⼀下关键的步骤也就⾏了。

分为以下⼏步来说明完成端⼝:0) 同步IO与异步IO1) 函数2) 常见问题以及解答3) 步骤4) 例程0、同步IO与异步IO同步I/O⾸先我们来看下同步I/O操作,同步I/O操作就是对于同⼀个I/O对象句柄在同⼀时刻只允许⼀个I/O操作,原理图如下:由图可知,内核开始处理I/O操作到结束的时间段是T2~T3,这个时间段中⽤户线程⼀直处于等待状态,如果这个时间段⽐较短,则不会有什么问题,但是如果时间⽐较长,那么这段时间线程会⼀直处于挂起状态,这就会很严重影响效率,所以我们可以考虑在这段时间做些事情。

DELPHI中完成端口(摘自掰掰开发)

DELPHI中完成端口(摘自掰掰开发)

DELPHI中完成端口(IOCP)的简单分析出自掰掰开发-/用DELPHI开发网络代码已经有一段时间了!我发现在网上用VC来实现完成端口(IOCP)的代码很多,但是使用DELPHI来实现的就比较少了。

对IOCP讲的清楚的就更少了。

在这里我把自己编写DELPHI下的IOCP写出来,希望对刚学完成端口的朋友有个帮助。

首先我们来了解一些在使用IOCP的时候需要使用的一些结构!(1):单IO数据结构LPVOID = Pointer;LPPER_IO_OPERATION_DATA = ^ PER_IO_OPERATION_DATA ;PER_IO_OPERATION_DATA = packed recordOverlapped: OVERLAPPED;DataBuf: TWSABUF;Buffer: array [0..1024] of CHAR;BytesSEND: DWORD;BytesRECV: DWORD;end;上面的结构中Overlapped: OVERLAPPED;和DataBuf: TWSABUF;是固定的结构类型。

Buffer: array [0..1024] of CHAR;是用来保存接受数据的缓存。

BytesSEND: DWORD;用来标志发送数据的长度。

BytesRECV: DWORD;用来标志接受数据的长度。

因为完成端口的工作者线程可以接受到来自客户端的数据,同时还可以接受到自己发送给客户端的数据,所以我们使用BytesSEND,BytesRECV变量来说是用来区分这次的数据是来自客户端的数据还是自己发送出去的数据。

详细的使用方法,我会在下面详细说明。

(2):“单句柄数据结构”LPPER_HANDLE_DATA = ^ PER_HANDLE_DATA;PER_HANDLE_DATA = packed recordSocket: TSocket;end;下来我从编写一个完成端口的为例说明。

在c#使用IOCP(完成端口)的简单示例

在c#使用IOCP(完成端口)的简单示例

在c#使用IOCP(完成端口)的简单示例这次给大家演示一下利用IOCP的在线程间传递数据的例子,顺便打算讲一些细节和注意的地方。

概述:这里主要使用IOCP的三个API,CreateIoCompletionPort,PostQueuedCompletionStatus,GetQueuedCompletionStatus,第一个是用来创建一个完成端口对象,第二个是向一个端口发送数据,第三个是接受数据,基本上用着三个函数,就可以写一个使用IOCP的简单示例。

其中完成端口一个内核对象,所以创建的时候会耗费性能,CPU得切换到内核模式,而且一旦创建了内核对象,我们都要记着要不用的时候显式的释放它的句柄,释放非托管资源的最佳实践肯定是使用Dispose模式,这个博客园有人讲过N次了。

而一般要获取一个内核对象的引用,最好用SafeHandle来引用它,这个类可以帮你管理引用计数,而且用它引用内核对象,代码更健壮,如果用指针引用内核对象,在创建成功内核对象并复制给指针这个时间段,如果抛了ThreadAbortException,这个内核对象就泄漏了,而用SafeHandle去应用内核对象就不会在赋值的时候发生ThreadAbortException。

另外SafeHandle类继承自CriticalFinalizerObject类,并实现了IDispose接口,CLR对CriticalFinalizerObject及其子类有特殊照顾,比如说在编译的时候优先编译,在调用非CriticalFinalizerObject类的Finalize方法后再调用CriticalFinalizerObject类的Finalize类的Finalize方法等。

在win32里,一般一个句柄是-1或者0的时候表示这个句柄是无效的,所以.net有一个SafeHandle的派生类SafeHandleZeroOrMinusOneIsInvalid ,但是这个类是一个抽象类,你要引用自己使用的内核对象或者非托管对象,要从这个类派生一个类并重写Relseas方法。

完成端口

完成端口

IOCP模型总结(转)2009-06-26 17:22IOCP(I/O Completion Port,I/O完成端口)是性能最好的一种I/O模型。

它是应用程序使用线程池处理异步I/O请求的一种机制。

在处理多个并发的异步I/O请求时,以往的模型都是在接收请求是创建一个线程来应答请求。

这样就有很多的线程并行地运行在系统中。

而这些线程都是可运行的,Windows内核花费大量的时间在进行线程的上下文切换,并没有多少时间花在线程运行上。

再加上创建新线程的开销比较大,所以造成了效率的低下。

调用的步骤如下:抽象出一个完成端口大概的处理流程:1:创建一个完成端口。

2:创建一个线程A。

3:A线程循环调用GetQueuedCompletionStatus()函数来得到IO操作结果,这个函数是个阻塞函数。

4:主线程循环里调用accept等待客户端连接上来。

5:主线程里accept返回新连接建立以后,把这个新的套接字句柄用CreateIoCompletionPort 关联到完成端口,然后发出一个异步的WSASend或者WSARecv调用,因为是异步函数,WSASend/WSARecv会马上返回,实际的发送或者接收数据的操作由WINDOWS系统去做。

6:主线程继续下一次循环,阻塞在accept这里等待客户端连接。

7:WINDOWS系统完成WSASend或者WSArecv的操作,把结果发到完成端口。

8:A线程里的GetQueuedCompletionStatus()马上返回,并从完成端口取得刚完成的WSASend/WSARecv的结果。

9:在A线程里对这些数据进行处理(如果处理过程很耗时,需要新开线程处理),然后接着发出WSASend/WSARecv,并继续下一次循环阻塞在GetQueuedCompletionStatus()这里。

归根到底概括完成端口模型一句话:我们不停地发出异步的WSASend/WSARecv IO操作,具体的IO处理过程由WINDOWS系统完成,WINDOWS系统完成实际的IO处理后,把结果送到完成端口上(如果有多个IO都完成了,那么就在完成端口那里排成一个队列)。

WinSock IO 模型 —- 完成端口模型IOCP

WinSock IO 模型 —- 完成端口模型IOCP

引言:经过三天的努力,我终于把Winsock I/O 模型,做了一个基本了解,最终还把最为复杂高效的CompletionPort(完成端口)模型看懂了,可以说这是一场比较大的胜利。

下面来着重介绍Completion Port(完成端口)。

一、学习前提:1、会winsock 基础编程,也就是说你要明白winsock 的Tcp 和udp编程流程。

2、学习完成端口前最好是先去好好看下重叠I/O 模型,因为完成端口模型,有一部分还是重叠I/O模型的东西,要是不懂那你就歇菜了。

3、会CreateThread 创建线程最好是明白原理。

二、模型特性:在使用一个技术前如果连这个技术的特性都不了解,那你就真是一个….。

1、首先要强调的是完成端口模型是只适合Window NT 或Windows2000 等windows系统,所以在其他系统上用不了。

2、完成端口模型是一种复杂但是可以用时管理数百甚至数千的套接字的一种模型所以当你的系统要同时处理非常多的通信请求完成端口是最好的选择。

3、一般我们使用完成端口的时候都是按照系统CPU数量来创建IO服务线程的,所以如果你的系统cpu数量越多那么性能当然会越好。

三、模型原理:这个模型原理其实是我个人的一个理解和概括,所以不一定完全正确各位看官见谅不要说我坑你了。

其实所谓的完成端口模型就是先创建一个完成端口(注意我们在创建完成端口的时候指定了这个完成端口同时执行的IO服务线程数量,一般为cpu数量),并创建若干(说若干是因为我们一般是根据cpu数量来创建线程的,这样做是因为每个cpu执行一个线程就避免的线程的切换影响效率,但是实际我们还是会创建大于cpu数量一定值的线程,虽然前面指定了完成端口同时执行的线程只有cpu个数,但是因为有的线程会出现挂起的情况,多创建一点就可以减少因为挂起而使cpu闲置)的服务线程(将完成端口作为参数传递给线程)然后将完成端口和我们关心的套接字关联在一起,并且在关联的时候我们还可以传递一个自定义的结构体。

IOCP完成端口详解

IOCP完成端口详解

IOCP完成端口详解通常要开发网络应用程序并不是一件轻松的事情,不过,实际上只要掌握几个关键的原则也就可以了——创建和连接一个套接字,尝试进行连接,然后收发数据。

真正难的是要写出一个可以接纳少则一个,多则数千个连接的网络应用程序。

本文将讨论如何通过Winsock2在Windows NT 和Windows 2000上开发高扩展能力的Winsock应用程序。

文章主要的焦点在客户机/服务器模型的服务器这一方,当然,其中的许多要点对模型的双方都适用。

API与响应规模通过Win32的重叠I/O机制,应用程序可以提请一项I/O操作,重叠的操作请求在后台完成,而同一时间提请操作的线程去做其他的事情。

等重叠操作完成后线程收到有关的通知。

这种机制对那些耗时的操作而言特别有用。

不过,像Windows 3.1上的WSAAsyncSelect()及Unix下的select()那样的函数虽然易于使用,但是它们不能满足响应规模的需要。

而完成端口机制是针对操作系统内部进行了优化,在Windows NT 和Windows 2000上,使用了完成端口的重叠I/O机制才能够真正扩大系统的响应规模。

完成端口一个完成端口其实就是一个通知队列,由操作系统把已经完成的重叠I/O请求的通知放入其中。

当某项I/O操作一旦完成,某个可以对该操作结果进行处理的工作者线程就会收到一则通知。

而套接字在被创建后,可以在任何时候与某个完成端口进行关联。

通常情况下,我们会在应用程序中创建一定数量的工作者线程来处理这些通知。

线程数量取决于应用程序的特定需要。

理想的情况是,线程数量等于处理器的数量,不过这也要求任何线程都不应该执行诸如同步读写、等待事件通知等阻塞型的操作,以免线程阻塞。

每个线程都将分到一定的CPU时间,在此期间该线程可以运行,然后另一个线程将分到一个时间片并开始执行。

如果某个线程执行了阻塞型的操作,操作系统将剥夺其未使用的剩余时间片并让其它线程开始执行。

完成端口(Completion Port)实现思路浅析

完成端口(Completion Port)实现思路浅析

完成端口是 NT 架构下一种高效的异步 IO 辅助机制,其使用方法已经被广为讨论,MSDN里面也有很详细的说明和示例。

《Windows网络编程》一书中有关于通过完成端口实现高效网络服务器设计的详细说明;《Windows核心编程》一书中有关于异步IO以及线程池等相关知识的使用介绍;而《Windows 2000 内部揭密》一书中则有对完成端口实现原理的简要介绍;此外一些关于 Win32 下多线程编程方面的书籍也对完成端口有所提及。

因此,对于完成端口的使用我这里就不再罗嗦,下面将直接从实现角度对其进行分析。

首先,从设计角度来看:完成端口是一个 NT 执行子系统的核心对象。

通过将完成端口与任意个 I/O 句柄(文件或Socket等)关联,使得用户可以通过完成端口这一统一途径,异步的获取并处理 I/O 操作的结果,同时能够最大限度利用多 CPU 的优势。

而且因为完成端口实际上是 NT 系统很多底层机制如 APC 的实现手段,故而在效率上是最高的,因为绝大多数其他类似解决方法最终还是使用完成端口实现。

与 WaitForMultipleObjects 函数不同,完成端口是由系统直接提供并行优化支持的,通过建立完成端口时指定的并行线程值,系统可以保证工作在同一完成端口上的线程数量受控(一般等于系统CPU数量),这样就可以避免无意义的线程上下文切换,获取更高的性能。

其次,从使用角度来看:完成端口使用 CreateIoCompletionPort 函数和 CloseHandle 函数创建和释放,使用 GetQueuedCompletionStatus 函数和 PostQueuedCompletionStatus 函数实现完成端口的读写操作。

因此我们的分析从这三个函数开始。

CreateIoCompletionPort 函数在 ExistingCompletionPort 参数为空的时候调用 NtCreateIoCompletion 函数(iocomplete.c:51)创建一个新的完成端口对象;然后处理 FileHandle 参数为 INVALID_HANDLE_VALUE 时的情况;最后调用 NtSetInformationFile 函数(dll estrfil.c:740)试图将 I/O 句柄绑定到完成端口上。

完成端口

完成端口

举例: 当文件handle被关联到完成端口,并且文 件有异步IO操作完成,操作系统就会创建 一个完成包(completion packet)放入此 完成端口消息队列中。工作线程检查此队 列,提取取完成包,对此io进行下一步操 作

三、完成端口的使用
单句柄数据: 单句柄数据是指和一个IO资源相关联的数据。 举例: 与一个文件handle或者一个套接字相关联的数据。
获得完成包的函数:
BOOL GetQueuedCompletionStatus( HANDLE CompletionPort, LPDWORD lpNumberOfBytesTransferred, LPDWORD CompletionKey, LPOVERLAPPED *lpOverlapped, DWORD dwMiillisecondTimeout);
struct DataPerHandle { socket sock ; char ip[16] ; unsigned short port }
单IO数据: 单IO数据是和每次IO操作相关联的数据 。 举例: 与文件写入或者套接字发送动作相关联的数据。
Struct DataPerIO { OVERLAPPED Overlapped ; char *pBuf ; int nbufLen; int OperationType; }
可以使用重叠IO的socket函数: WSASend WSARecv AcceptEx WSASendTo WSARecvFrom WSAIoctl
使用重叠IO时,每个函数必须以overlap结构 作为参数调用; 调用结束后,函数马上返回,如果IO操作尚未完成, 函数返回错误,错误号为WSA_IO_PENDING
int rtn = WSASend(…&(overlaped)…) ; if( rtn ==SOCKET_ERROR ) { if(GetLastError() != WSA_IO_PENDING ) { return rtn; } }

理解I O C P(完成端口)

理解I O C P(完成端口)

理解I/O Completion Port(完成端口)欢迎阅读此篇IOCP教程。

我将先给出IOCP的定义然后给出它的实现方法,最后剖析一个Echo 程序来为您拨开IOCP的谜云,除去你心中对IOCP的烦恼。

OK,但我不能保证你明白IOCP 的一切,但我会尽我最大的努力。

以下是我会在这篇文章中提到的相关技术:I/O端口同步/异步堵塞/非堵塞服务端/客户端多线程程序设计Winsock API 2.0在这之前,我曾经开发过一个项目,其中一块需要网络支持,当时还考虑到了代码的可移植性,只要使用select,connect,accept,listen,send还有recv,再加上几个#ifdef的封装以用来处理Winsock和BSD套接字[socket]中间的不兼容性,一个网络子系统只用了几个小时很少的代码就写出来了,至今还让我很回味。

那以后很长时间也就没再碰了。

前些日子,我们策划做一个网络游戏,我主动承担下网络这一块,想想这还不是小case,心里偷着乐啊。

网络游戏好啊,网络游戏为成百上千的玩家提供了乐趣和令人着秘的游戏体验,他们在线上互相战斗或是加入队伍去战胜共同的敌人。

我信心满满的准备开写我的网络,于是乎,发现过去的阻塞同步模式模式根本不能拿到一个巨量多玩家[MMP]的架构中去,直接被否定掉了。

于是乎,就有了IOCP,如果能过很轻易而举的搞掂IOCP,也就不会有这篇教程了。

下面请诸位跟随我进入正题。

什么是IOCP?先让我们看看对IOCP的评价I/O完成端口可能是Win32提供的最复杂的内核对象。

[Advanced Windows 3rd] Jeffrey Richter这是[IOCP]实现高容量网络服务器的最佳方法。

[Windows Sockets2.0:Write Scalable Winsock Apps Using Completion Ports]Microsoft Corporation完成端口模型提供了最好的伸缩性。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

IOCP完成端口超级详解目录:1.完成端口的优点2.完成端口程序的运行演示3.完成端口的相关概念4.完成端口的基本流程5.完成端口的使用详解6.实际应用中应该要注意的地方一.完成端口的优点1. 我想只要是写过或者想要写C/S模式网络服务器端的朋友,都应该或多或少的听过完成端口的大名吧,完成端口会充分利用Windows内核来进行I/O的调度,是用于C/S 通信模式中性能最好的网络通信模型,没有之一;甚至连和它性能接近的通信模型都没有。

2. 完成端口和其他网络通信方式最大的区别在哪里呢?(1) 首先,如果使用“同步”的方式来通信的话,这里说的同步的方式就是说所有的操作都在一个线程内顺序执行完成,这么做缺点是很明显的:因为同步的通信操作会阻塞住来自同一个线程的任何其他操作,只有这个操作完成了之后,后续的操作才可以完成;一个最明显的例子就是咱们在MFC的界面代码中,直接使用阻塞Socket调用的代码,整个界面都会因此而阻塞住没有响应!所以我们不得不为每一个通信的Socket都要建立一个线程,多麻烦?这不坑爹呢么?所以要写高性能的服务器程序,要求通信一定要是异步的。

(2) 各位读者肯定知道,可以使用使用“同步通信(阻塞通信)+多线程”的方式来改善(1)的情况,那么好,想一下,我们好不容易实现了让服务器端在每一个客户端连入之后,都要启动一个新的Thread和客户端进行通信,有多少个客户端,就需要启动多少个线程,对吧;但是由于这些线程都是处于运行状态,所以系统不得不在所有可运行的线程之间进行上下文的切换,我们自己是没啥感觉,但是CPU却痛苦不堪了,因为线程切换是相当浪费CPU时间的,如果客户端的连入线程过多,这就会弄得CPU都忙着去切换线程了,根本没有多少时间去执行线程体了,所以效率是非常低下的,承认坑爹了不?(3) 而微软提出完成端口模型的初衷,就是为了解决这种"one-thread-per-client"的缺点的,它充分利用内核对象的调度,只使用少量的几个线程来处理和客户端的所有通信,消除了无谓的线程上下文切换,最大限度的提高了网络通信的性能,这种神奇的效果具体是如何实现的请看下文。

3. 完成端口被广泛的应用于各个高性能服务器程序上,例如著名的Apache….如果你想要编写的服务器端需要同时处理的并发客户端连接数量有数百上千个的话,那不用纠结了,就是它了。

二.完成端口程序的运行演示首先,我们先来看一下完成端口在笔者的PC机上的运行表现,笔者的PC配置如下:大体就是i7 2600 + 16GB内存,我以这台PC作为服务器,简单的进行了如下的测试,通过Client生成3万个并发线程同时连接至Server,然后每个线程每隔3秒钟发送一次数据,一共发送3次,然后观察服务器端的CPU和内存的占用情况。

如图2所示,是客户端3万个并发线程发送共发送9万条数据的log截图图3是服务器端接收完毕3万个并发线程和每个线程的3份数据后的log截图最关键是图4,图4是服务器端在接收到28000个并发线程的时候,CPU占用率的截图,使用的软件是大名鼎鼎的Process Explorer,因为相对来讲这个比自带的任务管理器要准确和精确一些。

我们可以发现一个令人惊讶的结果,采用了完成端口的Server程序(蓝色横线所示)所占用的CPU才为 3.82%,整个运行过程中的峰值也没有超过4%,是相当气定神闲的……哦,对了,这还是在Debug环境下运行的情况,如果采用Release方式执行,性能肯定还会更高一些,除此以外,在UI上显示信息也很大成都上影响了性能。

相反采用了多个并发线程的Client程序(紫色横线所示)居然占用的CPU高达11.53%,甚至超过了Server程序的数倍……其实无论是哪种网络操模型,对于内存占用都是差不多的,真正的差别就在于CPU 的占用,其他的网络模型都需要更多的CPU动力来支撑同样的连接数据。

虽然这远远算不上服务器极限压力测试,但是从中也可以看出来完成端口的实力,而且这种方式比纯粹靠多线程的方式实现并发资源占用率要低得多。

三.完成端口的相关概念在开始编码之前,我们先来讨论一下和完成端口相关的一些概念,如果你没有耐心看完这段大段的文字的话,也可以跳过这一节直接去看下下一节的具体实现部分,但是这一节中涉及到的基本概念你还是有必要了解一下的,而且你也更能知道为什么有那么多的网络编程模式不用,非得要用这么又复杂又难以理解的完成端口呢??也会坚定你继续学习下去的信心^_^3.1 异步通信机制及其几种实现方式的比较我们从前面的文字中了解到,高性能服务器程序使用异步通信机制是必须的。

而对于异步的概念,为了方便后面文字的理解,这里还是再次简单的描述一下:异步通信就是在咱们与外部的I/O设备进行打交道的时候,我们都知道外部设备的I/O和CPU比起来简直是龟速,比如硬盘读写、网络通信等等,我们没有必要在咱们自己的线程里面等待着I/O操作完成再执行后续的代码,而是将这个请求交给设备的驱动程序自己去处理,我们的线程可以继续做其他更重要的事情,大体的流程如下图所示:我可以从图中看到一个很明显的并行操作的过程,而“同步”的通信方式是在进行网络操作的时候,主线程就挂起了,主线程要等待网络操作完成之后,才能继续执行后续的代码,就是说要么执行主线程,要么执行网络操作,是没法这样并行的;“异步”方式无疑比“阻塞模式+多线程”的方式效率要高的多,这也是前者为什么叫“异步”,后者为什么叫“同步”的原因了,因为不需要等待网络操作完成再执行别的操作。

而在Windows中实现异步的机制同样有好几种,而这其中的区别,关键就在于图1中的最后一步“通知应用程序处理网络数据”上了,因为实现操作系统调用设备驱动程序去接收数据的操作都是一样的,关键就是在于如何去通知应用程序来拿数据。

它们之间的具体区别我这里多讲几点,文字有点多,如果没兴趣深入研究的朋友可以跳过下一面的这一段,不影响的:)(1) 设备内核对象,使用设备内核对象来协调数据的发送请求和接收数据协调,也就是说通过设置设备内核对象的状态,在设备接收数据完成后,马上触发这个内核对象,然后让接收数据的线程收到通知,但是这种方式太原始了,接收数据的线程为了能够知道内核对象是否被触发了,还是得不停的挂起等待,这简直是根本就没有用嘛,太低级了,有木有?所以在这里就略过不提了,各位读者要是没明白是怎么回事也不用深究了,总之没有什么用。

(2) 事件内核对象,利用事件内核对象来实现I/O操作完成的通知,其实这种方式其实就是我以前写文章的时候提到的《基于事件通知的重叠I/O模型》,链接在这里,这种机制就先进得多,可以同时等待多个I/O操作的完成,实现真正的异步,但是缺点也是很明显的,既然用WaitForMultipleObjects()来等待Event的话,就会受到64个Event等待上限的限制,但是这可不是说我们只能处理来自于64个客户端的Socket,而是这是属于在一个设备内核对象上等待的64个事件内核对象,也就是说,我们在一个线程内,可以同时监控64个重叠I/O操作的完成状态,当然我们同样可以使用多个线程的方式来满足无限多个重叠I/O的需求,比如如果想要支持3万个连接,就得需要500多个线程…用起来太麻烦让人感觉不爽;(3) 使用APC( Asynchronous Procedure Call,异步过程调用)来完成,这个也就是我以前在文章里提到的《基于完成例程的重叠I/O模型》,链接在这里,这种方式的好处就是在于摆脱了基于事件通知方式的64个事件上限的限制,但是缺点也是有的,就是发出请求的线程必须得要自己去处理接收请求,哪怕是这个线程发出了很多发送或者接收数据的请求,但是其他的线程都闲着…,这个线程也还是得自己来处理自己发出去的这些请求,没有人来帮忙…这就有一个负载均衡问题,显然性能没有达到最优化。

(4) 完成端口,不用说大家也知道了,最后的压轴戏就是使用完成端口,对比上面几种机制,完成端口的做法是这样的:事先开好几个线程,你有几个CPU我就开几个,首先是避免了线程的上下文切换,因为线程想要执行的时候,总有CPU资源可用,然后让这几个线程等着,等到有用户请求来到的时候,就把这些请求都加入到一个公共消息队列中去,然后这几个开好的线程就排队逐一去从消息队列中取出消息并加以处理,这种方式就很优雅的实现了异步通信和负载均衡的问题,因为它提供了一种机制来使用几个线程“公平的”处理来自于多个客户端的输入/输出,并且线程如果没事干的时候也会被系统挂起,不会占用CPU周期,挺完美的一个解决方案,不是吗?哦,对了,这个关键的作为交换的消息队列,就是完成端口。

比较完毕之后,熟悉网络编程的朋友可能会问到,为什么没有提到WSAAsyncSelect 或者是WSAEventSelect这两个异步模型呢,对于这两个模型,我不知道其内部是如何实现的,但是这其中一定没有用到Overlapped机制,就不能算作是真正的异步,可能是其内部自己在维护一个消息队列吧,总之这两个模式虽然实现了异步的接收,但是却不能进行异步的发送,这就很明显说明问题了,我想其内部的实现一定和完成端口是迥异的,并且,完成端口非常厚道,因为它是先把用户数据接收回来之后再通知用户直接来取就好了,而WSAAsyncSelect和WSAEventSelect之流只是会接收到数据到达的通知,而只能由应用程序自己再另外去recv数据,性能上的差距就更明显了。

最后,我的建议是,想要使用基于事件通知的重叠I/O和基于完成例程的重叠I/O的朋友,如果不是特别必要,就不要去使用了,因为这两种方式不仅使用和理解起来也不算简单,而且还有性能上的明显瓶颈,何不就再努力一下使用完成端口呢?3.2 重叠结构(OVERLAPPED)我们从上一小节中得知,要实现异步通信,必须要用到一个很风骚的I/O数据结构,叫重叠结构“Overlapped”,Windows里所有的异步通信都是基于它的,完成端口也不例外。

至于为什么叫Overlapped?Jeffrey Richter的解释是因为“执行I/O请求的时间与线程执行其他任务的时间是重叠(overlapped)的”,从这个名字我们也可能看得出来重叠结构发明的初衷了,对于重叠结构的内部细节我这里就不过多的解释了,就把它当成和其他内核对象一样,不需要深究其实现机制,只要会使用就可以了,想要了解更多重叠结构内部的朋友,请去翻阅Jeffrey Richter的《Windows via C/C++》5th的292页,如果没有机会的话,也可以随便翻翻我以前写的Overlapped的东西,不过写得比较浅显……这里我想要解释的是,这个重叠结构是异步通信机制实现的一个核心数据结构,因为你看到后面的代码你会发现,几乎所有的网络操作例如发送/接收之类的,都会用WSASend()和WSARecv()代替,参数里面都会附带一个重叠结构,这是为什么呢?因为重叠结构我们可以理解成为是一个网络操作的ID号,也就是说我们要利用重叠I/O提供的异步机制的话,每一个网络操作都要有一个唯一的ID号,因为进了系统内核,里面黑灯瞎火的,也不了解上面出了什么状况,一看到有重叠I/O的调用进来了,就会使用其异步机制,并且操作系统就只能靠这个重叠结构带有的ID号来区分是哪一个网络操作了,然后内核里面处理完毕之后,根据这个ID号,把对应的数据传上去。

相关文档
最新文档