完成端口模型的使用与分析

来源 :软件 | 被引量 : 0次 | 上传用户:lpflpf7337
下载到本地 , 更方便阅读
声明 : 本文档内容版权归属内容提供方 , 如果您对本文有版权争议 , 可与客服联系进行内容授权或下架
论文部分内容阅读
  摘 要:完成端口是一种复杂的Win32内核对象。应用程序可以用完成端口管理线程池为大量的异步I/O请求提供服务,而不必为每个I/O请求分别创建服务线程。完成端口模型用于开发服务器应用程序,以提供优良的伸缩性和获得最好的系统性能。本文构建了一个基本的网络服务器程序框架,对完成端口模型的用法进行阐述与分析。
  关键词:完成端口;线程池;套接字
  中图分类号:TP311 文献标识码:a DoI: 10.3969/j.issn.1003-6970.2012.02.010
  The Usage and Analysis of Completion Port Model ZHoU Peng,HUaNG Can,JIaNG Nan(Unit 91550 of PLA, Dalian 116023, Liaoning)
  【Abstract】Completion port is a complicated win32 kernel object. applications can provide service for a large number of
  asynchronous I/O requests by using completion port to manage thread pool, and needn’t create service threads for each I/O request. Completion port model is used to develop server application to provide excellent scalability and get the best system performance. This paper constructed a basic network server application framework, expatiated and analyzed the usage of Completion port model.
  【Key words】completion port; thread pool; socket
   0 引言
  在Win32中,套接字是一个指向传输提供者的句柄[1]。Winsock提供了锁定和非锁定两种套接字模式,以决定Winsock函数随套接字调用时的行为。锁定模式虽然容易使用,但对于建立连接的多个套接字,或数据收发量不均,时间不规律的情况,却难以管理;而非锁定模式则难以处理Winsock函数调用时可能会收到的WSAEWOULDBLOCK错误。
  为了解决套接字模式存在的限制,Winsock提供了一些套接字模型,帮助应用程序通过异步方式,一次对一个或多个套接字上进行的I/O操作加以管理。这些模型包括:select(选择)、WSAAsyncSelect(异步选择)、WSAEventSelect(事件选择)、Overlapped I/O(重叠I/O)和Completion port(完成端口)。
  相对于其它套接字模型,完成端口模型是最复杂的、也是伸缩性最好的一种模型。当应用程序作为服务器为大量套接字的I/O请求提供服务时,使用这种模型会获得最好的系统性能。许多高性能服务器,如Apache、IIS等,都使用了完成端口模型的技术[2]。因此,掌握完成端口模型的用法和工作机制是非常必要的。
   1 完成端口模型的使用和分析
  下面将构建一个基本的网络服务器程序框架,对使用完成端口模型管理大量套接字的方法加以论述。程序框架的构建步骤如下:
  (1)创建一个完成端口;
  (2)创建线程池;
  (3)创建套接字监听客户连接请求;
  (4)为完成端口关联连接返回的套接字;
  (5)在建立的连接上进行I/O操作。
   1.1 创建完成端口
  要创建完成端口,应调用CreateIoCompletionPort函数:HANDLE CreateIoCompletionPort(HANDLE hFile,
  HANDLE hExistingCompletionport,
  DWORD dwCompletionKey,
  DWORD dwNumberOfConcurrentThreads);该函数完成两种不同的工作:(1)创建一个完成端口;(2)把一个设备同完成端口相关联。如果只创建一个完成端口,可以这样调用函数:
  HANDLE hIocp = CreateIoCompletionPort(INVALID_ HANDLE_VALUE, NULL, 0, 0);
  参数dwNumberOfConcurrentThreads定义了完成端口能够同时运行的最多线程数。如果该参数为0,完成端口缺省允许的并发线程数目是计算机上的CPU数目。这样,每个
   CPU可以运行一个线程,以避免多余的线程上下文切换,节省CPU的周期。如果处理一个客户请求需要一段比较长的时间,为了提高应用程序的伸缩性,可能需要增大该值。
   1.2 创建线程池
  成功创建完成端口对象之后,就需要创建一个线程池来处理客户请求。虽然创建线程时系统资源的开销比较小,但远非可以忽略不计。在程序初始化时创建一个线程池,可以避免运行时创建线程。如果这些线程在应用程序执行期间是空闲的,程序的性能就能进一步提高。
  线程池中的线程数量应该有一个上限,因为创建太多的线程会浪费系统资源。对于池中应该有多少线程,微软公司给出的参考答案是将计算机上的CPU数目乘以2。值得注意的是,应该使线程池中至少有一个线程能接受客户请求。如果池中所有的线程都在忙,就可能拒绝客户请求,所以最好是用更多的上下文切换接受和处理客户请求。
  如果计算机有两个CPU,那么应该创建4个线程等待完成的I/O请求:
  HANDLE hThread = NULL; for (int n=0; n<4; ++n)
  hThread = CreateThread(NULL, 0, WorkerThread, hIocp, 0, NULL);
  其中,WorkerThread是处理客户请求的服务线程。
   1.3 创建套接字监听客户连接请求
  创建一个监听套接字:
  SOCKET sListen = WSASocket(AF_INET, SOCK_ STREAM, 0, NULL, 0,
  WSA_FLAG_OVERLAPPED);绑定监听套接字到本机地址,端口号为5150:struct sockaddr_in saServer;
  saServer.sin_family = AF_INET;
  saServer.sin_addr.s_addr = inet_addr(INADDR_ ANY);
  saServer.sin_port = htons(5150);
  bind(sListen, (SOCKADDR *)&saServer, sizeof(saServer));
  套接字监听客户连接请求,允许的最大连接数为200:listen(sListen, 200);
   1.4 为完成端口关联连接返回的套接字
  监听套接字在接受客户连接请求后,返回一个建立真正连接的新套接字。调用CreateIoCompletionPort函数为完成端口对象关联这个新套接字。函数的前两个参数分别是套接字和完成端口的对象句柄,第三个参数dwCompletionKey称为“完成键”,是一个32位值的参数,通常用作指针指向一个描述套接字的相关信息的结构。应用程序把套接字关联到完成端口后,就可以通过该端口管理套接字上的异步I/O请求。
  定义描述套接字相关信息的结构:
  
  接受一个连接请求,返回套接字关联到完成端口上,然后投递一个I/O请求: while (true) { struct sockaddr_in saRemote; int nSaLen = sizeof(saRemote);
  SOCKET sNew = accept(sListen, (sockaddr *)&saRemote,&nSaLen);
  LPIOREQ pIoReq = new IOREQ; pIoReq->s = sNew;
  pIoReq->op = OP_READ;
  CreateIoCompletionPort((HANDLE)ioreq->s, hIocp, (DWORD)pIoReq, 0);
  PostRecvReq(pIoReq);
  }
  为了避免定义的IOREQ结构变量作为临时变量被系统回收而导致应用程序出错,应该使用new操作符为该变量分配堆内存,其地址将作为完成端口的完成键传递套接字的相关信息。
   1.5 在建立的连接上进行I/O操作
  主线程创建完线程池后,池中的线程调用GetQueued CompletionStatus函数进入空闲状态,等待完成端口上的完成的I/O请求。
  当一个套接字上的异步I/O请求完成时,系统检查该套接字是否关联了一个完成端口。如果是,该端口对象内部的I/O完成队列加入一个完成的I/O请求项。当线程池中运行的线程数目小于完成端口允许并发线程的最大数目,该端口就唤醒一个空闲线程处理完成的I/O请求,然后按照先进先出(FIFO)的顺序从I/O完成队列中移除被处理的I/O请求项。
  值得注意的是,对线程池中空闲线程的调用是按照后进先出(LIFO)顺序的。如果I/O请求完成的足够慢,完成端口就总是唤醒同一个线程,而没有被调度的线程的内存资源就可以被交换到磁盘上和从CPU缓存中清除,这样会有效地提高应用程序的性能。
  
  
  如果线程处理了一个失败的I/O请求,应该使用delete操作符从堆内存中释放相应的IOREQ结构变量,以防止因内存泄露而导致系统资源耗尽。
   2 总结
  完成端口模型的设计非常合理,它组合了重叠I/O和独立线程的特性[3]。使用线程池处理异步I/O请求,就是用少量线程管理大量的套接字,而不需要为每个客户端创建服务线程处理异步I/O请求,这样就大大节省了创建线程和切换线程上下文的系统开销。另外,完成端口使用后进先出的方式调用服务线程,也会有效地减少对系统资源的使用。因此,对于处理大量I/O请求的服务器程序,应该优先考虑使用完成端口模型,以获得良好的伸缩性和最好的系统性能。
  参考文献
  [1] 陈坚,陈伟,等. Visual C++网络高级编程[M]. 北京:人民邮电出版社,2001.
  [2] 王艳平,张越. Windows网络与通信程序设计[M]. 北京:人民邮电出版社,2006.
  [3] HART J M. Windows系统编程(原书第三版)[M]. 安娜,吴明军. 北京:机械工业出版社,2006.
其他文献
目的:探讨切除术后乳腺癌患者三氧治疗前后血清胸苷激酶1(thymidine kinase-1,TK1)水平的变化及其与肿瘤分期的关系。方法:应用酶免疫点印记化学发光法检测104例切除术后乳腺癌患
探究试题的背景、推演命题历程,有助于加深对试题所考查的知识本质的理解.通过对2017年高考浙江卷的压轴试题的探究,发现它以数列不动点为知识背景,构造收敛数列,再引入函数
随着互联网的发展,传统的图书馆服务受到挑战。文章从建立量化考核指标的指导思想、基本思路、组织与实施等几个方面,初步探讨了正德职业技术学院图书馆实施量化考核的方法,
为充分掌握厦门市中小企业服务体系的发展现状和存在问题,我们面向仝市展开了调查工作。调杏内容涵盖了八个方面,即:受访企业基本情况、信息化水平、技术水平与研发工作、企业融
一、活动目标 (一) 认识大衣的简单结构 (二) 学习穿大衣的方法,懂得自己的事情自己做,培养自理能力。二、活动准备图片4张,大班幼儿2名,大衣每位幼儿一件。三、活动过程 (一
ue*M#’#dkB4##8#”专利申请号:00109“7公开号:1278062申请日:00.06.23公开日:00.12.27申请人地址:(100084川C京市海淀区清华园申请人:清华大学发明人:隋森芳文摘:本发明属于生物技
功能化敷料(Functional Wound Dressing,FWD)与传统敷料的本质区别在于,敷料材质的设计不只是舒适的覆盖并保护创面,而是朝向功能化发展:快速帮助创面愈合及针对不同创面类型