IOCP 的意思和SQLSERVER里的IOCP等待 全称 IO_COMPLETION

IOCP  的意思和SQLSERVER里的IOCP等待 全称 IO_COMPLETION

http://baike.baidu.com/view/1256215.htm?fr=aladdin

IOCP编辑

 
本词条缺少概述信息栏名片图,补充相关内容使词条更完整,还能快速升级,赶紧来编辑吧!
 
 

1基本概念编辑

IOCP全称I/O Completion Port,中文译为I/O完成端口IOCP是一个异步I/O的API,它可以高效地将I/O事件通知给应用程序。与使用select()或是其它异步方法不同的是,一个套接字[socket]与一个完成端口关联了起来,然后就可继续进行正常的Winsock操作了。然而,当一个事件发生的时候,此完成端口就将被操作系统加入一个队列中。然后应用程序可以对核心层进行查询以得到此完成端口

2详细概念编辑

这里我要对上面的一些概念略作补充,在解释[完成]两字之前,我想先简单的提一下同步和异步这两个概念,逻辑上来讲做完一件事后再去做另一件事就是同步,而同时一起做两件或两件以上事的话就是异步了。你也可以拿单线程和多线程来作比喻。但是我们一定要将同步和堵塞,异步和非堵塞区分开来,所谓的堵塞函数诸如accept(…),当调用此函数后,此时线程将挂起,直到操作系统来通知它,“HEY兄弟,有人连进来了”,那个挂起的线程将继续进行工作,也就符合”生产者-消费者”模型。堵塞和同步看上去有两分相似,但却是完全不同的概念。大家都知道I/O设备是个相对慢速的设备,不论打印机,调制解调器,甚至硬盘,与CPU相比都是奇慢无比的,坐下来等I/O的完成是一件不甚明智的事情,有时候数据的流动率非常惊人,把数据从你的文件服务器中以Ethernet速度搬走,其速度可能高达每秒一百万字节,如果你尝试从文件服务器中读取100KB,在用户的眼光来看几乎是瞬间完成,但是,要知道,你的线程执行这个命令,已经浪费了10个一百万次CPU周期。所以说,我们一般使用另一个线程来进行I/O。重叠IO[overlapped I/O]是Win32的一项技术,你可以要求操作系统为你传送数据,并且在传送完毕时通知你。这也就是[完成]的含义。这项技术使你的程序在I/O进行过程中仍然能够继续处理事务。事实上,操作系统内部正是以线程来完成overlapped I/O。你可以获得线程所有利益,而不需要付出什么痛苦的代价。
完成端口中所谓的[端口]并不是我们在TCP/IP中所提到的端口,可以说是完全没有关系。我到现在也没想通一个I/O设备[I/O Device]和端口[IOCP中的Port]有什么关系。估计这个端口也迷惑了不少人。IOCP只不过是用来进行读写操作,和文件I/O倒是有些类似。既然是一个读写设备,我们所能要求它的只是在处理读与写上的高效。
下面是一个VC++实现IOCP服务器框架的流程:
当然,IOCP也是基于Winsock2套接字库的,所以在开始先必须初始化套接字库。
1
2
3
4
//初始化套接字库
 
WSADATAwsaData;
WSAStartup(MAKEWORD(2,2),&wsaData);
IOCP除了需一个服务器套接字外,还需一个完成端口的句柄,该句柄直接调用API
1
2
3
4
5
//创建IOCP句柄
HANDLEm_hIocp=CreateIoCompletionPort(INVALID_HANDLE_VALUE,0,0,0);
 
//创建服务器套接字,这里要注意的是最后一个参数必须为:WSA_FLAG_OVERLAPPED重叠模式
SOCKETm_Server=WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
之后为套接字绑定一个本地端口,用来监听客户端的连接
1
2
3
4
5
SOCKADDR_INaddr;
addr.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
addr.sin_family=AF_INET;
addr.sin_port=htons(2000);
bind(m_Server,(SOCKADDR*)&addr,sizeof(SOCKADDR);
绑定端口之后,建立一个监听线程,用来监听客户端的连接,当有连接进来时,将该连接的套接字加入到IOCP对队列中,同时再创建几个工作线程,这样在该连接发生请求时,IOCP模型就会在工作线程通知,这样我们就可以在工作线程中,完成对客户端的请求做出一系列响应。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//监听线程流程
UINTWINAPIAcceptThreadProc(LPVOIDlpParameter){
sockaddr_insockAddr={0};
intlen=sizeof(sockAddr);
while(TRUE){
SOCKETsNew=WSAAccept(m_Server,(sockaddr*)&sockAddr,&len,NULL,NULL);
//将客户端加入到IOCP队列中
CreateIoCompletionPort((HANDLE)sNew,m_hIocp,ULONG_PTR(sNew),0);
//这里要注意一下,对网上一些相关的资料,一些新手在写IOCP时
//会发现得不到用户请求及一些事件,那是因为没有对该用户投递一个接收IO
//IOCP的工作原理就是,必须对一个连接投递一个接收IO,处理完一个,再投递一个...
OVERLAPPEDPLUS*ol;//....对OL结构进行初始化后
intnRet=WSARecv(ol->s,&(ol->wsaBuf),1,&(ol->dwBytes),&(ol->dwFlags),&(ol->ol),NULL);
intnError=WSAGetLastError();
//在投递接收IO时。返回值要注意。如果为nError==ERROR_IO_PENDING说明已经投递成功。
}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//工作线程流程
 
UINTWINAPIWorkerThreadProc(LPVOIDlpParameter)
 
{
 
DWORDCompleteBytes=0;
 
DWORDdwKey=0;
 
POVERLAPPEDPLUSol=NULL;
 
BOOLbRet=GetQueuedCompletionStatus(m_hIocp,&CompleteBytes,(PULONG_PTR)&dwKey,(LPOVERLAPPED*)&ol,INFINITE);
 
if(!bRet)
 
{
 
//客户端断开了连接
 
}
 
 
 
//如果为接收时CompleteBytes为实际收到的大,发送时为实际发送的大小
 
switch(ol->OpCode)
 
{
 
caseIOCP_RECV:
 
//....处理接收请求
 
break;
 
caseIOCP_SEND:
 
//....处理发送请求
 
break;
 
}
 
}
工作线程中循环调用了 GetQueuedCompletionStatus 这个API来取出当前活动的套接字,当套接字有数据应请求或者断开时。该函数也会立即返回。
如果要对工作线程停止,PostQueuedCompletionStatus 使用该API 向工作发送停止命令,工作线程收到此命令后,可以安全退出线程。
以上就是大致实现流程,仅限于VC++开发使用,由于其它平台语言实现起来有些困难,可以采用一些第三方做的 IOCP支持库,如网上现比较好用的有易语言和C++版的,IOCP服务器模型支持库,该第三方库写的比较好用。可以直接使用的。还有一些其它版本的。实现原理也是以上方法。

 

 

SELECT * FROM sys.[dm_os_wait_stats] WHERE [wait_type] LIKE '%io_%'

--IO_COMPLETION    640    50813    591    195
--ASYNC_IO_COMPLETION    1    1020    1020    0


简介

  IOCP模型属于一种通讯模型,适用于Windows平台下高负载服务器的一个技术。在处理大量用户并发请求时,如果采用一个用户一个线程的方式那将造成CPU在这成千上万的线程间进行切换,后果是不可想象的。而IOCP完成端口模型则完全不会如此处理,它的理论是并行的线程数量必须有一个上限-也就是说同时发出500个客户请求,不应该允许出现500个可运行的线程。目前来说,IOCP完成端口是Windows下性能最好的I/O模型,同时它也是最复杂的内核对象。它避免了大量用户并发时原有模型采用的方式,极大的提高了程序的并行处理能力。

原理图

  从图中可以看到,一共包括三部分:完成端口(存放重叠的I/O请求),客户端请求的处理,等待者线程队列(一定数量的工作者线程,一般采用CPU*2个)。

  完成端口中所谓的[端口]并不是我们在TCP/IP中所提到的端口,可以说是完全没有关系。它其实就是一个通知队列,由操作系统把已经完成的重叠I/O请求的通知放入其中。当某项I/O操作一旦完成,某个可以对该操作结果进行处理的工作者线程就会收到一则通知。

  通常情况下,我们会在创建一定数量的工作者线程来处理这些通知,也就是线程池的方法。线程数量取决于应用程序的特定需要。理想的情况是,线程数量等于处理器的数量,不过这也要求任何线程都不应该执行诸如同步读写、等待事件通知等阻塞型的操作,以免线程阻塞。每个线程都将分到一定的CPU时间,在此期间该线程可以运行,然后另一个线程将分到一个时间片并开始执行。如果某个线程执行了阻塞型的操作,操作系统将剥夺其未使用的剩余时间片并让其它线程开始执行。也就是说,前一个线程没有充分使用其时间片,当发生这样的情况时,应用程序应该准备其它线程来充分利用这些时间片。

IOCP的优点

  基于IOCP的开发是异步IO的,决定了IOCP所实现的服务器的高吞吐量。

  完成端口的线程并发量可以在创建该完成端口时指定,从而限制了与该完成端口相关联的可运行线程的数目。

     通过引入IOCP,会大大减少Thread切换带来的额外开销,最小化的线程上下文切换,减少线程切换带来的巨大开销,让CPU把大量的事件用于线程的运行。当与该完成端口相关联的可运行线程的总数目达到了该并发量,系统就会阻塞任何与该完成端口相关联的后续线程的执行,直到与该完成端口相关联的可运行线程数目下降到小于该并发量为止。

  Select是先查询再发起IO请求,IOCP是先发起IO请求再接收通知。但是Select方式在处理大量非活动连接时是比较低效的,因为每次Select需要对所有的Socket状态进行查询,而对非活动的Socket查询是没有意义的浪费,另外由于Socket句柄不能设置用户私有数据,当查询返回Socket句柄时还需要一个额外的查询来找到关联的用户对象,这两点是Select低效的关键。

 

http://www.cnblogs.com/coder2012/p/3185715.html


 

MSSQL WIKI

Non-yielding IOCP Listener, Non-yielding Scheduler and non-yielding resource monitor known issues and fixes

 

http://mssqlwiki.com/2012/08/21/non-yielding-iocp-listener-non-yielding-scheduler-and-non-yielding-resource-monitor-known-issues-and-fixes/

 

posted @ 2014-09-16 14:22  桦仔  阅读(1648)  评论(0编辑  收藏  举报