IOCP原理

[C++ 网络协议] IOCP(Input Output Completion Port) IOCP的介绍 阅读详情
AI摘要

本文旨在深入探讨IOCP(I/O Completion Port)的本质和工作原理,从计算机的青铜时代引入中断的概念,阐述如何在现代操作系统中利用IOCP模型高效地管理输入输出操作,特别关注于如何克服硬件与CPU速度差异带来的效率瓶颈。通过详细解析创建IOCP、定义线程池函数、创建线程以及将IO操作句柄绑定到IOCP的过程,文章提供了一个全面的指南,帮助开发者掌握IOCP的应用。此外,文章还讨论了如何与IOCP线程池交互以及如何停止线程池工作,进一步丰富了IOCP编程实践的知识库。

要想彻底征服IOCP,并应用好IOCP这个模型,首先就让我们穿越到遥远的计算机青铜器时 代(以出现PC为标志),那时候普通的PC安装的还是DOS平台,微软公司主要靠这个操作系统在IT界的原始丛林中打拼,在DOS中编写程序,不得不与很 多的硬件直接打交道,而最常操作的硬件无非是键盘、声显卡、硬盘等等,这些设备都有一个特点就是速度慢,当然是相对于PC平台核心CPU的速度而言,尤其 是硬盘这个机械电子设备,其速度对于完全电子化得CPU来说简直是“相对静止”的设备。很多时候CPU可以干完n件(n>1000)事情的时间中, 这些硬件可能还没有完成一件事情,显然让CPU和这些硬件同步工作将是一种严重的浪费,并且也不太可能,此时,聪明的硬件设计师们发明了一种叫做中断的操 作方式,用以匹配这种速度上的严重差异。中断工作的基本原理就是,CPU首先设置一个类似回调函数的入口地址,其次CPU对某个硬件发出一个指令,此时 CPU就去干别的活计了,最后那个慢的象蜗牛一样的硬件执行完那个指令后,就通知CPU,让CPU暂时“中断”手头的工作,去调用那个“回调函数”。至此 一个完整的中断调用就结束了。这个模型曾经解决了显卡与CPU不同步的问题,最重要的是解决了硬盘速度与CPU速度严重不匹配的问题,并因此还派生出了更 有名的DMA(直接内存访问技术,主要是指慢速硬件可以读写原本只能由CPU直接读写的内存)硬盘IO方式。(注意这里说的中断工作方式只是中断工作方式 的一种,并不是全部,详细的中断原理请参阅其它专业文献。)

其实“中断”方式更 像是一种管理模型,比如在一个公司中,如果要老板时时刻刻盯着员工作事情,那么除非是超人,否则无人能够胜任,同时对于老板这个稀缺资源来说也是一种极起 严重的浪费。更多时候老板只是发指令给员工,然后员工去执行,而老板就可以做别的事情,或者干脆去打高尔夫休息,当员工完成了任务就会通过电话、短信、甚 至e-mail等通知老板,此时老板就去完成一个响应过程,比如总结、奖罚、发出新指令等等。由此也看出如果一个公司的“老板占用率”(类似CPU占用 率)太高,那么就说明两种情况:要么是它的员工很高效,单位时间内完成的指令非常多;要么是公司还没有建立有效的“中断”响应模型。如果你的公司是后者, 那么你就可以试着用这个模型改造公司的管理了,由此你可以晋升到管理层,而不用再去管你的服务端程序有没有使用IOCP了,呵呵呵。

如果真的搞明白了这个传说中的“中断”操作方式,那么理解IOCP的基本原理就不费劲了。

结 束了计算机的青铜时代后,让我们穿越到现在这个“计算机蒸汽”时代,(注意不是“计算机IT”时代,因为计算机还没法自己编写程序让自己去解决问题)。在 现代,Windows几乎成了PC平台上的标准系统,而PC平台上的几大件还是没有太大的变化,除了速度越来越快。而因为操作系统的美妙封装,我们也不用 再去直接同硬件打交道了,当然编写驱动程序的除外。

在Windows平台上,我 们不断的调用着WriteFile和ReadFile这些抽象的函数,操作着“文件”这种抽象的信息集合,很多时候调用这些函数时,是以一种“准同步”的 方式操作硬件的,比如要向一个文件中写入1M的信息,只有等到WriteFile函数返回,操作才算结束,这个过程中,我们的程序则类似死机一样,等待硬 盘写入操作的结束(实际是被系统切换出了当前的CPU时间片)。于此同时,调用了WriteFile的线程则无法干别的任何事情。因为整个线程是在以一种 称为过程化的模型中运行,所有的处理流程全部是线性的。对于程序的流畅编写来说,线性化的东西是一个非常好的东西,甚至几乎早期很多标准的算法都是基于程 序是过程化得这一假设而设计的。而对于一些多任务、多线程环境来说,这种线性的工作方式会使系统严重低效,甚至造成严重的浪费,尤其在现代多核CPU已成 为主流的时候,显然让一个CPU内核去等待另一个CPU内核完成某事后再去工作,是非常愚蠢的一种做法。

面 对这种情况,很多程序员的选择是多线程,也就是专门让一个线程去进行读写操作,而别的线程继续工作,以绕开这些看起来像死机一样的函数,但是这个读写线程 本身还是以一种与硬盘同步的方式工作的。然而这并不是解决问题的最终方法。我们可以想象一个繁忙的数据库系统,要不断的读写硬盘上的文件,可能在短短的一 秒钟时间就要调用n多次WriteFile或ReadFile,假设这是一个网站的后台数据库,那么这样的读写操作有时还可能都是较大的数据块,比如网站 的图片就是比较典型的大块型数据,这时显然一个读写线程也是忙不过来的,因为很有可能一个写操作还没有结束,就会又有读写操作请求进入,这时读写线程几乎 变成了无响应的一个线程,可以想象这种情况下,程序可能几乎总在瘫痪状态,所有其它的线程都要等待读写操作线程完活。也许你会想多建n个线程来进行读写操 作,其实这种情况会更糟糕,因为不管你有多少线程,先不说浪费了多少系统资源,而你读写的可能是相同的一块硬盘,只有一条通道,结果依然是一样的,想象硬 盘是独木桥,而有很多人(线程)等着过桥的情形,你就知道这更是一个糟糕的情形。所以说在慢速的IO面前,多线程往往不是“万灵丹”。

面 对这种情形,微软公司为Windows系统专门建立了一种类似“青铜时代”的中断方式的模型来解决这个问题。当然,不能再像那个年代那样直接操作硬件了, 需要的是旧瓶装新酒了。微软是如何做到的呢,实际还是通过“回调函数”来解决这个问题的,大致也就是要我们去实现一个类似回调函数的过程,主要用于处理来 自系统的一些输入输出操作“完成”的通知,相当于一个“中断”,然后就可以在过程中做输入输出完成的一些操作了。比如在IO操作完成后删除缓冲,继续发出 下一个命令,或者关闭文件,设备等。实际上从逻辑的角度来讲,我们依然可以按照线性的方法来分析整个过程,只不过这是需要考虑的是两个不同的函数过程之间 的线性关系,第一个函数是发出IO操作的调用者,而第二个函数则是在完成IO操作之后的被调用者,。而被调用的这个函数在输入输出过程中是不活动的,也不 占用线程资源,它只是个过程(其实就是个函数,内存中的一段代码而已)。调用这些函数则需要一个线程的上下文,实际也就是一个函数调用栈,很多时候,系统 会借用你进程空间中线程来调用这个过程,当然前提条件是事先将可以被利用的线程设置成“可警告”状态,这也是线程可警告状态的全部意义,也就是大多数内核 同步等待函数bAlertable(有些书翻译做可警告的,我认为应该理解为对IO操作是一种“时刻警惕”的状态)参数被传递TRUE值之后的效果。比 如:WaitForSingleObjectEx、SleepEx等等。

当然上 面说的这种方式其实是一种“借用线程”的方式,当进程中没有线程可借,或者可借的线程本身也比较忙碌的时候,会造成严重的线程争用情况,从而造成整体性能 低下,这个方式的局限性也就显现出来了。注意“可警告”状态的线程,并不总是在可以被借用的状态,它们本身往往也需要完成一些工作,而它调用一些能够让它 进入等待状态的函数时,才可以被系统借用,否则还是不能被借用的。当然借用线程时因为系统有效的保护了栈环境和寄存器环境,所以被借用的线程再被还回时线 程环境是不会被破坏的。

鉴于借用的线程的不方便和不专业,我们更希望通过明确的 “创建”一批专门的线程来调用这些回调函数(为了能够更深入的理解,可以将借用的线程想象成出租车,而将专门的线程想象成私家车),因此微软就发明了 IOCP“完成端口”这种线程池模型,注意IOCP本质是一种线程池的模型,当然这个线程池的核心工作就是去调用IO操作完成时的回调函数,这就叫专业! 这也是IOCP名字的来由,这就比借用线程的方式要更加高效和专业,因为这些线程是专门创建来做此工作的,所以不用担心它们还会去做别的工作,而造成忙碌 或不响应回调函数的情况,另外因为IO操作毕竟是慢速的操作,所以几个线程就已经足可以应付成千上万的输入输出完成操作的请求了(还有一个前提就是你的回 调函数做的工作要足够少),所以这个模型的性能是非常高的。也是现在Windows平台上性能最好的输入输出模型。它首先就被用来处理硬盘操作的输入输 出,同时它也支持邮槽、管道、甚至WinSock的网络输入输出。

至此对于完成端口的本质原理应该有了一个比较好的理解,尤其是掌握了IOCP是线程池模型的这一本质,那么对于之后的IOCP实际应用就不会有太多的疑问了。接下去就让我们从实际编程的角度来了解一下IOCP,也为彻底掌握IOCP编程打下坚实的基础。

要应用IOCP,首先就要我们创建一个叫做IOCP的内核对象,这需要通过CreateIoCompletionPort这个函数来创建,这个函数的原型如下:

HANDLE WINAPI CreateIoCompletionPort(

  __in          HANDLE FileHandle,

  __in          HANDLE ExistingCompletionPort,

  __in          ULONG_PTR CompletionKey,

  __in          DWORD NumberOfConcurrentThreads

);

这 个函数是个本身具有多重功能的函数(Windows平台上这样的函数并不多),需要用不同的方式来调用,以实现不同的功能,它的第一个功能正如其名字所描 述的“Create”,就是创建一个完成端口的内核对象,要让他完成这个功能,只需要指定NumberOfConcurrentThreads参数即可, 前三个参数在这种情况下是没有意义的,只需要全部传递NULL即可,象下面这样我们就创建了一个完成端口的内核对象:

HANDLE hICP = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,1);

这 里首先解释下为什么第一个参数不是NULL而是INVALID_HANDLE_VALUE,因为第一个参数按照定义是一个文件的句柄,也就是需要IOCP 操作的文件句柄,而代表“NULL”文件句柄的实际值是INVALID_HANDLE_VALUE,这是因为NULL实际等于0,而0这个文件句柄被用于 特殊用途,所以要用INVALID_HANDLE_VALUE来代表“NULL”意义的文件,INVALID_HANDLE_VALUE的值是-1或者 0xFFFFFFFF。

最后一个参数 NumberOfConcurrentThreads就有必要好好细细的说说了,因为很多文章中对于这个参数总是说的含糊其辞,不知所云,有些文章中甚至 人云亦云的说赋值为CPU个数的2倍即可,所谓知其然,不知其所以然。其实这个参数的真实含义就是“真正并发同时执行的最大线程数”,这个并发是真并发, 怎么去理解呢,如果你有两颗CPU,而你赋值为2那么就是说,在每颗CPU上执行一个线程,并且真正的并发同时执行,当然如果你设置了比CPU数量更大的 数值,它的含义就变成了一个理论并发值,而实际系统的最大可能的严格意义上的并发线程数就是CPU个数,也就是你在任务管理器中看到的CPU个数(可能是 物理个数,也可能是内核个数,还有可能是超线程个数,或者它们的积)。讲到这里大家也许就有疑问了,为什么有些文章资料中说要设置成CPU个数的2倍呢? 这通常是一个半经验值,因为大多数IOCP完成回调的过程中,需要一些逻辑处理,有些是业务性的,有些要访问数据库,有些还可能访问硬盘,有些可能需要进 行数据显示等等,无论哪种处理,这总是要花费时间的,而系统发现你设置了超过CPU个数的并发值时,那么它就尽可能的来回切换这些线程,使他们在一个时间 段内看起来像是并发的,比如在1ms的时间周期内,同时有4个IOCP线程被调用,那么从1ms这段时间来看的话,可以认为是有4个线程被并发执行了,当 然时间可以无限被细分,真并发和模拟并发实际就是针对时间细分的粒度来说的。这样一来如何设置并发数就是个设计决策问题,决策的依据就是你的回调函数究竟 要干些什么活,如果是时间较长的活计,就要考虑切换其它线程池来完成,如果是等待性质的活计,比如访问硬盘,等待某个事件等,就可以设置高一点的并发值, 强制系统切换线程造成“伪并发”,如果是非常快速的活计,那么就直接设置CPU个数的并发数就行了,这时候防止线程频繁切换是首要任务。当然并发数最好是 跟踪调试一下后再做决定,默认的推荐值就是CPU个数的2倍了。(绕了一大圈我还是“人云亦云”了一下,哎呦!谁扔的砖头?!)

上 面的全部就是创建一个完成端口对象,接下来就是打造线程了,打造的方法地球人都知道了,就是CreateThread,当然按照人云亦云的说法应该替之以 _beginthread或_beginthreadex,原因嘛?你想知道?真的想知道?好了看你这么诚恳的看到了这里,那就告诉你吧,原因其实就是因 为我们使用的语言从本质上说是C/C++,很多时候我们需要在线程函数中调用很多的C/C++味很重的库函 数,而有些函数是在Windows诞生以前甚至是多线程多任务诞生以前就诞生了,这些老爷级的函数很多都没有考虑过多线程安全性,还有就是C++的全局对 象静态对象等都需要调用它们的构造函数来初始化,而调用的主体就是线程,基于这些原因就要使用C/C++封装过的创建线程函数来创建线程,而 CreateThread始终是Windows系统的API而已,它是不会考虑每种语言环境的特殊细节的,它只考虑系统的环境。

好了让我们继续打造线程的话题,要创建线程,实际核心就是准备一个线程函数,原型如下:

1、使用CreateThread时:

DWORD WINAPI ThreadProc(LPVOID lpParameter);

2、使用_beginthread时:

void __cdecl ThreadProc( void * pParameter );

3、使用_beginthreadex时:

unsigned int __stdcall ThreadProc(void* pParam);

其实上面三个函数原型都是很简单的,定义一个线程函数并不是什么难事,而真正困难的是对线程的理解和定义一个好的线程函数。这里我就不在多去谈论关于线程原理和如何写好一个线程函数的内容了,大家可以去参阅相关的文献。

现 在我们接着讨论IOCP的专用线程如何编写,IOCP专用线程编写的核心工作就是调用一个同步函数GetQueuedCompletionStatus, 为了理解的方便性,你可以想象这个函数的工作原理与那个有名的GetMessage是类似的,虽然这种比喻可能不太确切,但是他们工作方式是有些类似的地 方,它们都会使调用它们的线程进入一种等待状态,只是这个函数不是等待消息队列中的消息,它是用来等待“被排队的完成状态”(就是它名字的含义)的,排队 的完成状态,其实就是IO操作完成的通知(别告诉我你还不知道什么是IO操作),如果当前没有IO完成的通知,那么这个函数就会让线程进入“等待状态”, 实际也就是一种“可警告”的状态,这样系统线程调度模块就会登记这个线程,一旦有IO完成通知,系统就会“激活”这个线程,立即分配时间片,让该线程开始 继续执行,已完成IO完成通知的相关操作。

首先让我看看GetQueuedCompletionStatus的函数原型:

BOOL WINAPI GetQueuedCompletionStatus(

  __in          HANDLE CompletionPort,

  __out         LPDWORD lpNumberOfBytes,

  __out         PULONG_PTR lpCompletionKey,

  __out         LPOVERLAPPED* lpOverlapped,

  __in          DWORD dwMilliseconds

);

第一个参数就是我们之前创建的那个完成端口内核对象的句柄,这个参数实际也就是告诉系统,我们当前的线程是归哪个完成端口对象来调度。

第二个参数是一个比较有用的参数,在函数返回后它将告诉我们这一次的IO操作实际传输或者接收了多少个字节的信息,这对于我们校验数据收发完整性非常有用。

第 三个参数是与完成端口句柄绑定的一个一对一的数据指针,当然这个数据是我们绑到这个完成端口句柄上的,其实这个参数也是类似本人博客文章中所提到的那个 “火车头”的作用的,它的作用和意义就是在我们得到完成通知时,可以拿到我们在最开初创建完成端口对象时绑定到句柄上的一个自定义的数据。这里给一个提示 就是,在用C++的类封装中,通常这个参数我们会在绑定时传递类的this指针,而在GetQueuedCompletionStatus返回时又可以拿 到这个类的this指针,从而可以在这个完成线程中调用类的方法。

第四个参数就是在本人其它IOCP相关博文中详细介绍过的重叠操作的数据结构,它也是一个火车头,这里就不在赘述它的用法了,请大家查阅本人其它博文拙作。

第 五个参数是一个等待的毫秒数,也就是GetQueuedCompletionStatus函数等待IO完成通知的一个最大时间长度,如果超过这个时间 值,GetQueuedCompletionStatus就会返回,并且返回值一个0值,此时调用GetLastError函数会得到一个明确的 WAIT_TIMEOUT,也就是说它等待超时了,也没有等到一个IO完成通知。这时我们可以做一些相应的处理,而最常见的就是再次调用 GetQueuedCompletionStatus函数让线程进入IO完成通知的等待状态。当然我们可以传递一个INFINITE值,表示让此函数一直 等待,直到有一个完成通知进入完成状态队列。当然也可以为这个参数传递0值,表示该函数不必等待,直接返回,此时他的工作方式有些类似 PeekMessage函数。

函数的参数和原型都搞清楚了,下面就让我们来看看调用的例子:

UINT CALLBACK IOCPThread(void* pParam)

{

CoInitialize(NULL);

       DWORD dwBytesTrans = 0;

       DWORD dwPerData = 0;

LPOVERLAPPED lpOverlapped = NULL;

       while(1)

       {

              BOOL bRet = GetQueuedCompletionStatus( hICP,&dwBytesTrans

,&dwPerData,&lpOverlapped,INFINITE);

              if( NULL == lpOverlapped )

              {

                     DWORD dwError = GetLastError();

                     ......//错误处理

}

              PMYOVERLAPPED pMyOL

= CONTAINING_RECORD(lpOverlapped, MYOVERLAPPED, m_ol);

              if( !HasOverlappedIoCompleted(lpOverlapped) )

              {//检测到不是一个真正完成的状态

                     DWORD dwError = GetLastError();

                     ......//错误处理

              }

                     ...... //继续处理

}

       return 0;

}

在这个线程函数中,我们写了一个死循环,这个是必要的,因为这个线程要反复处理IO完成通知的操作。跟我们常见的消息循环是异曲同工。

有 了线程函数,接着就是创建线程了,对于IOCP来说,创建多少线程其实是一个决策问题,一般的原则就是创建的实际线程数量,不应小于调用 CreateIoCompletionPort创建完成端口对象时指定的那个最大并发线程数。一般的指导原则是:如果完成线程的任务比较繁重大多数情况下 执行的是其它的慢速等待性质的操作(比如磁盘磁带读写操作,数据库查询操作,屏幕显示等)时,由于这些操作的特点,我们可以适当的提高初始创建的线程数 量。但是如果是执行计算密集型的操作时(比如网游服务端的场景变换运算,科学计算,工程运算等等),就不易再靠增加线程数来提高性能,因为这类运算会比较 耗费CPU,没法切换出当前CPU时间片,多余的线程反倒会造成因为频繁的线程切换而造成整个程序响应性能的下降,此时为了保证IOCP的响应性,可以考 虑再建立线程池来接力数据专门进行计算,这也是我的博文《IOCP编程之“双节棍”》篇中介绍的用线程池接力进行计算并提高性能的思想的核心。

下面的例子展示了如何创建IOCP线程池中的线程:

SYSTEM_INFO si = {};

GetSystemInfo(&si);

//创建CPU个数个IOCP线程

for( int i = 0; i < si.dwNumberOfProcessors; i ++ )

{

UINT nThreadID = 0;

//以暂停的状态创建线程状态

HANDLE hThread = (HANDLE)_beginthreadex(NULL,0,IOCPThread

,(void*)pThreadData,CREATE_SUSPENDED,(UINT*)&nThreadID);

//然后判断创建是否成功

if( NULL == reinterpret_cast<UINT>(m_hThread)

                     || 0xFFFFFFFF == reinterpret_cast<UINT>(m_hThread) )

{//创建线程失败

              ......//错误处理

}

::ResumeThread(hThread);//启动线程

}

创 建好了IOCP的线程池,就可以往IOCP线程池中添加用来等待完成的那些重叠IO操作的句柄了,比如:重叠IO方式的文件句柄,重叠IO操作方式的 SOCKET句柄,重叠IO操作的命名(匿名)管道等等。上面的这个操作可以被称作将句柄绑定到IOCP,绑定的方法就是再次调用 CreateIoCompletionPort函数,这次调用时,就需要明确的指定前两个参数了,例子如下:

//创建一个重叠IO方式的SOCKET

SOCKET skSocket = ::WSASocket(AF_INET,SOCK_STREAM,IPPROTO_IP,

                            NULL,0,WSA_FLAG_OVERLAPPED);

......//其它操作

//绑定到IOCP

CreateIoCompletionPort((HANDLE)skSocket,hICP,NULL,0);

由代码就可以看出这步操作就非常的简单了,直接再次调用CreateIoCompletionPort函数即可,只是这次调用的意义就不是创建一个完成端口对象了,而是将一个重叠IO方式的对象句柄绑定到已创建好的完成端口对象上。

至此整个IOCP的基础知识算是介绍完了,作为总结,可以回顾下几个关键步骤:

1、  用CreateIoCompletionPort创建完成端口;

2、  定义IOCP线程池函数,类似消息循环那样写一个“死循环”调用GetQueuedCompletionStatus函数,并编写处理代码;

3、  创建线程;

4、  将重叠IO方式的对象句柄绑定到IOCP上。

只要记住了上面4个关键步骤,那么使用IOCP就基本掌握了。最后作为补充,让我再来讨论下这个核心步骤之外的一些附带的步骤。

现在假设我们已经创建了一个这样的IOCP线程池,而且这个线程池也工作的非常好了,那么我们该如何与这个线程池中的线程进行交互呢?还有就是我们如何让这个线程池停下来?

其 实这个问题可以很简单的来思考,既然IOCP线程池核心的线程函数中有一个类似消息循环的结构,那么是不是也有一个类似PostMessage之类的函数 来向其发送消息,从而实现与IOCP线程的交互呢?答案是肯定的,这个函数就是PostQueuedCompletionStatus,现在看到它的名 字,你应该已经猜到它的用途了吧?对了,它就是用来向这个类似消息循环的循环中发送自定义的“消息”的,当然,它不是真正的消息,而是一个模拟的“完成状 态”。这个函数的原型如下:

BOOL WINAPI PostQueuedCompletionStatus(

  __in          HANDLE CompletionPort,

  __in          DWORD dwNumberOfBytesTransferred,

  __in          ULONG_PTR dwCompletionKey,

  __in          LPOVERLAPPED lpOverlapped

);

它 的参数与GetQueuedCompletionStatus类似,其实为了理解上的简单,我们可以认为 PostQueuedCompletionStatus的参数就是原样的被copy到了GetQueuedCompletionStatus,怎么调用这 个函数就应该可以理解了。通常在需要停止整个IOCP线程池工作时,就可以调用这个函数发送一个特殊的标志,比如设定dwCompletionKey为 NULL,并且在自定义lpOverlapped指针结构之后带上一个表示关闭的标志等。这样在线程函数中就可以通过判定这些条件而明确的知道当前线程池 需要关闭。当然也可以定义其它的操作扩展码来指定IOCP线程池执行指定的操作。下面的例子代码演示了如何发送一个IO完成状态:

MYOVERLAPPED *pOL = new MYOVERLAPPED ;

.......//其它初始化代码

pOL->m_iOpCode = OP_CLOSE;//指定关闭操作码

.......

PostQueuedCompletionStatus(hICP,0,NULL,(LPOVERLAPPED)pOL);

至 此IOCP的基础性的支持算是介绍完了,本篇文章的主要目的是为了让大家理解IOCP的本质和工作原理,为轻松驾驭IOCP这个编程模型打下坚实的基础。 最终需要掌握的就是认识到IOCP其实就是一个管理IO操作的自定义线程池这一本质。实际编码时决策性的问题就是理解最大并发数和预创建线程数的意义,并 根据实际情况设定一个合理的值。


Windows下的IOCP模型(二):IOCP使用示例 一 我对IOCP的使用     IOCP以异步处理网络I/O事件、优秀的线程调度等机制,成为Windows环境下性能最优秀的网络通信模型之一。但结合不同的应用场景,IOCP也需要合理的使用方式才能发挥其性能优势。     前段时间需要开发一个Windows环境下的网络通信综合集成系统,作者通过IOCP模型,设计实现了一个用于网络通信的底层模块,这个模块同时支持tcp、udp、广播、udp组播等多种通信方式,每种通信方式对应不同的事件处理机制,能够支持高性能的网络通信,并为上层模块提供服务。 二 支持多通信方 阅读详情

相关推荐

异步通信之IOCP详解

一、 概述 学习完网络基础,在写C/S应用程序时,大多童靴写服务器基本都没有用到io模型,基本都是采用“accept同步拥塞通讯和多线程方式”与客户端通讯。但当有成千上万客户端请求连接并与服务器通讯时,多线程的创建与CPU上下文的切换,服务器端压力可想而知,在资源有限的情况在,选择一个好的io模型才能搭建高性能服务器。其中IOCP广泛运用于个高性能服务器程序,apache服务器就是IOCP实现。 同步通讯和异步通信 在写网络程序时,我们知道CPU运行速度非常快,而在与IO设备进行数据交换时速度简直不忍直视。

对自己笑 4156

IOCP模型总结(转)

IOCP(I/O Completion Port,I/O完成端口)是性能最好的一种I/O模型。它是应用程序使用线程池处理异步I/O请求的一种机制。在处理多个并发的异步I/O请求时,以往的模型都是在接收请求是创建一个线程来应答请求。这样就有很多的线程并行地运行在系统中。而这些线程都是可运行的,Windows内核花费大量的时间在进行线程的上下文切换,并没有多少时间花在线程运行上。再加上创建新线程的开销

gxj1680的专栏 1009

ESP32S3开发实战:从Vscode+IDF环境配置到OpenOCD单步调试全流程

本文详细介绍了ESP32S3开发环境的完整搭建流程,重点讲解如何在Vscode中配置ESP-IDF开发框架,并利用OpenOCD工具实现硬件单步调试。内容涵盖从环境准备、工程创建、代码烧录到高级调试技巧的全方位实战指南,旨在帮助开发者快速掌握ESP32S3的高效开发与调试方法。

weixin_29168813的博客 208

IOCP技术详解

这几周我接触了Windows网络通讯中的IOCP模型,自己在网上找了相关的知识进行学习,自己又下了好多服务器端的代码,但都运行不了,也是自己菜,能力还需加强。幸好我师父资助了我一个能运行的服务端IOCP代码,自己参照网上的相关知识后又与这个能运行的代码做了参照,算是勉强理解了构造IOCP的一般方法,对IOCP的使用也有了很大的心得。接下来我就把自己对IOCP相关知识的理解记录下来,方便自己以后的复习,当然这篇文章如果对正在阅读的你有帮助也算是很好的。...

StarWang的博客 5798

【学习K230-例程46】GT6700-UVC摄像头

UVC模块提供了USB摄像头的检测、配置和图像采集功能,支持单摄像头操作。本实验通过开发板上的USB接口连接摄像头,将摄像头捕获到的画面显示在显示设备上。print(f"模式{i}: {mode.width}x{mode.height} {mode.format}@{mode.fps}fps")print(f"摄像头检测: {‘已连接’ if plugin else ‘未连接’}, 设备信息: {devinfo}")检测系统是否连接了USB摄像头(当前仅支持单个摄像头检测)。获取摄像头支持的所有视频模式。

GilgameshJSS的博客 807

Jenkins离线安装保姆级教程:从下载.war包到SVN+Maven项目一键部署(附避坑脚本)

本文详细介绍了Jenkins离线安装的全流程,包括.war包下载、SVN+Maven项目配置及自动化部署。特别针对内网环境提供解决方案,涵盖插件管理、开发工具链配置、构建触发器和SSH部署等关键步骤,帮助企业在网络隔离环境下实现高效持续集成。

weixin_30096975的博客 374

iocp原理

IOCP原理 要想彻底征服IOCP,并应用好IOCP这个模型,首先就让我们穿越到遥远的计算机青铜器时 代(以出现PC为标志),那时候普通的PC安装的还是DOS平台,微软公司主要靠这个操作系统在IT界的原始丛林中打拼,在DOS中编写程序,不得不与很 多的硬件直接打交道,而最常操作的硬件无非是键盘、声显卡、硬盘等等,这些设备都有一个特点就是速度慢,当然是相对于PC平台核心CPU的速度而言,尤其

u012219045的专栏 752

使用Python,OpenCV查找图像中的最亮点

这篇博客展示了,为什么在图像中找到最亮的点之前应用高斯模糊是至关重要的。 通过应用高斯模糊,可以将每个给定半径内的像素平均在一起。取平均值可以去除高频噪声,否则会影响cv2.minMaxLoc函数的结果。 一定要研究高斯模糊半径的恰当值。如果取值太小,将会减轻平均值的影响,而忽略了更大、更亮的区域。但是如果半径太大,将无法探测到小的明亮区域。

qq_40985985的博客 3809

IOCP工作原理

我已经有两次面试被问到iocp的工作原理了(操作系统的工作原理,不是函数的工作原理),所以觉得很有必要把这个写下来。网上找了半天,写的都是一些函数的实现原理,没提到重点,最近一次面试中技术官告诉我《windows高级编程指南》这本书有提到过,我回来查看了一下,在614页的确有比较详细的讲解,有点不太明白,先把原文引出来吧: 总结:完成端口的目标是使在释放线程(运行中的线程)列表中的线程数

qq51931373的专栏 1281

2026年美赛数学建模竞赛 MCM B题:使用太空电梯系统建立月球殖民地 问题一 月球殖民地材料运输的三种场景建模 详细解题思路和代码

如果运输系统不能完美工作(例如,系绳摆动、火箭失败、电梯故障等),你的解决方案在多大程度上会发生变化?技术可行性:三种方案在技术上都是可行的,但挑战不同 经济性:混合方案在长期视角下最具成本效益 时间效率:并行运输可以显著缩短总建设时间环境影响:太空电梯的环境优势明显,混合方案可平衡需求风险分散:混合方案提供了天然的风险分散机制

YOLO 616

【全局路径规划模块运动学算法】无人机Kinodynamic A*算法、Kinodynamic RRT*算法(附C++代码)

认知有限,望大家多多包涵,有什么问题也希望能够与大家多交流,共同成长!本文先对Kinodynamic A*算法做个简单的介绍,具体内容后续再更,其他模块可以参考去我其他文章提示:以下是本篇文章正文内容啃这个Kinodynamic A_star算法,建议先啃一啃高飞fast_planner那篇论文,把理论打好,如果还是比较迷,就先看Dijksta、再看A_star、再看hybrid A_star 三个的流程,因为往上资料比较多,再看这个Kinodynamic A_star算法,流程已经很相像了。如果。

qq_35635374的博客 2600

iocp的原理及其实现

iocp(I/O Completion Port),它是一种在windows下实现的网络编程模型(proactor)在操作IO的时候,socket与iocp进行绑定后,可以将io操作的投递堆叠在一起,就可以向iocp发很多io操作。将其存在请求队列里面。具体实现如下图:在很多接口中都有体现。

wwdxhcm的博客 686

华为OD机试双机位C卷-流水线(Py/Java/C/C++/Js/Go)

华为OD机试双机位C卷 流水线,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解。华为OD机试 - 流水线。

qq_45776114的博客 2266

深入剖析IOCP:从原理到实践

IOCP(Input/Output Completion Ports,输入/输出完成端口)是Windows操作系统中一种用于处理高效异步I/O操作的机制。它广泛应用于服务器端应用程序,如Web服务器和数据库服务器,能够有效处理大量的并发I/O请求。

人心有反复,好在山水有重逢。 2213

Win11 系统下 VisualStudio2022 配置点云库 PCL1.11.1

安装完成之后打开文件夹E:\PCL 1.11.1\3rdParty\OpenNI2双击 并选择路径 (E:\PCL 1.11.1\3rdParty\OpenNI2) 安装即可全部安装完成之后,将pcl-1.11.1-pdb-msvc2019-win64.zip解压后的 .pdb文件拷贝到(E:\PCL 1.11.1\bin)中设置环境变量:到此,环境变量配置完成。

qq_53545309的博客 1127

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

IOCP编程之基本原理

写在最前:这是一篇总结的非常不错的文章,可惜一路追查下去,却未能找到原创作者。从手笔来看,怀疑是大宝哥,但没有真凭实据。希望知情人士告知,谢过~一转:http://www.cppblog.com/hello8706/archive/2010/06/24/118663.aspx二转:http://blog.youkuaiyun.com/wangguang246/archive/2010/07/02/5708579.aspx在我的博客之前写了很多关于IOCP的“行云流水”似的看了让人发狂的文章,尤其是几篇关于IOCP加线程

优秀技术文章收录 2254

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

iocp 原理解析

转载 要想彻底征服IOCP,并应用好IOCP这个模型,首先就让我们穿越到遥远的计算机青铜器时 代(以出现PC为标志),那时候普通的PC安装的还是DOS平台,微软公司主要靠这个操作系统在IT界的原始丛林中打拼,在DOS中编写程序,不得不与很 多的硬件直接打交道,而最常操作的硬件无非是键盘、声显卡、硬盘等等,这些设备都有一个特点就是速度慢,当然是相对于PC平台核心CPU的速度而言,尤其 是硬盘这

在梦中享一刻静谧,等待梦中的你 899

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

上一篇: 1IOCP背景知识--重叠I/O理解
下一篇: SYN攻击实现
markman101
博客等级 码龄19年 199粉丝 116原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值