Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1466740
  • 博文数量: 218
  • 博客积分: 6394
  • 博客等级: 准将
  • 技术积分: 2563
  • 用 户 组: 普通用户
  • 注册时间: 2008-02-08 15:33
个人简介

持之以恒

文章分类

全部博文(218)

文章存档

2013年(8)

2012年(2)

2011年(21)

2010年(55)

2009年(116)

2008年(16)

分类: 网络与安全

2010-01-20 23:15:51

转:http://isealy.blog.163.com/blog/static/359756622009429112251463/

部分图自己重新的画了一下,原来的看不很清楚

加了一个LF线程池的图
我的另外一篇关于网络的文章:

http://blog.chinaunix.net/u2/61062/showart.php?id=2138339

1.UNIX网络I/O模型

1.1阻塞I/O

        Socket设置为阻塞模式,当socket不能立即完成I/O操作时,进程或线程进入等待状态,直到操作完成。如下图:


         这种模型非常经典,也被广泛使用,优势在于非常简单,等待的过程中占用的系统资源微乎其微,程序调用返回时,必定可以拿到数据;
         但简单也带来一些缺点,程序在数据到来并准备好以前,不能进行其他操作,需要有一个线程专门用于等待,这种代价对于需要处理大量连接的服务器而言,是很难接受的;

1.2非阻塞I/O

         socket设置成非阻塞模式,与阻塞模式不同的是:无数据时,也不会进入等待,而是立即返回特定错误,如下图:


    这种模式在没有数据可以接收时,可以进行其他的一些操作,比如有多个socket时,可以去查看其他socket有没有可以接收的数据;
    实际应用中,这种I/O模型的直接使用并不常见,因为它需要不停的查询,而这些查询大部分会是无必要的调用,白白浪费了系统资源;非阻塞I/O应该算是一个铺垫,为I/O复用和信号驱动奠定了非阻塞使用的基础。
1.3 I/O复用
         I/O复用模型能让一个或多个socket可读或可写准备好时,应用能被通知到;I/O复用模型早期用select实现,它的工作流程如下图:

         这种模型的使用场景一般有这样一个共同特点:都有多个socket需要处理,这样能在获取I/O事件时复用同一个等待机制。比如监听服务器,既要处理监听的socket,又要处理连接的socket
         I/O复用是应用场景较多的一种模式,socket连接数多时,大多会采用它。除了select以外,I/O复用的还可以用pollepollkqueuefreebsd)来实现,后两者在处理大量连接时性能上有很大的提高。
 1.4信号驱动
         信号驱动模型是在socket准备好的时候用信号的方式进行通知,然后应用程序从内核读取数据。
         然而,对于socketSIGIO触发意味着多种可能,对于UDP有两种,对于TCP,则有7种,要想区分是何种操作引起的signal都是一件困难的事情,所以这种模型很少被实用,直到内核2.3起,引入了POSIX RT-Signal机制以后,这一现象得到些许改善。

1.5异步I/O

         在标准Unix下,异步I/O是由 提供的,它把一个信号和值与每一个I/O操作关联起来。异步I/OPOSIX 1003.1b实时标准的扩展,也属于Single Unix Specificationversion 2
         几年前,Ben LaHaise实现了Linux AIO,合并到了2.5.32的内核中,在2.6时它正式成为标准特性。然而,令人遗憾的是,它目前还不支持对socket的操作,相信不久以后会完善起来。
         异步I/O的模型与I/O复用和信号驱动颇有些相似,但最大的区别是:信号到达时,I/O操作已经由内核完成,应用只需要继续处理数据就好;
         POSIXAIO的操作流程如下:

2.Windows网络I/O模型

         WindowsI/O模型与UNIX有一些类似的地方,差异也少:
         阻塞I/O、非阻塞I/O I/O复用使用起来基本一样;
        windowssignal不支持SIGIO,这样也就没有了基于signal的信号驱动模型,不过windows利用自身的窗口句柄和Event也做出了类似的东西,它们分别是WSAAyncSelectWSAEventSelect
         对于异步I/Owindows已经有了自己的解决方案,并且支持对socket的操作,那就是是IOCP

2.1WSAAyncSelectWSAEventSelect

         WSAAyncSelect用窗口句柄和自定义的消息,来传递socket事件,典型的windows处理逻辑。当READWRITEACCEPT等事件发生时,与Socket句柄绑定的窗口会收到指定的消息,消息的参数可以判断发生了什么样的网络事件;
         WSAEventSelectWSAAyncSelect不同,它不用窗口而用Event来传递socket事件,同时用WSAWaitForMultipleEventsEvent等待到Event事件,再通过WSAEnumNetworkEvents获取到底有哪些网络事件发生了;需要注意的是:由于系统的限制,WSAWaitXXX函数里waitEvent最多只能有64个。
         两者的相同之处在于,收到网络事件以后,它们还需要主动去操作数据;
         性能上,使用Event来通知肯定更为迅速。

2.2Overlapped I/O

         Windows下的异步IOIOCP的基础,就像非阻塞I/OI/O复用的基础一样;
重叠I/O的数据结构体如下,注意其中有一个Event对象
typedef struct _WSAOVERLAPPED {
  DWORD Internal;
  DWORD InternalHigh;
  DWORD Offset;
  DWORD OffsetHigh;
  WSAEVENT hEvent;
} WSAOVERLAPPED;
在创建socket时,声明该socket为重叠模式,此时需创建一个WSAOVERLAPPED对象,当调用发送或者接收时,把这个对象传入;
然后,可以等待WSAOVERLAPPED对象中的Event来获知网络事件,这个过程与WSAEventSelect有些类似,与之不同的是要使用WSAGetOverlappedResult来获取重叠IO返回的结果(注意,这里获取的是结果,也就是说当Event触发时,它已经把数据准备好了,是异步IO的处理方法);
         虽然Overlapped I/O已经是异步IO,但它在处理大量并发请求时比WSAEventSelect好不了多少,这样就有了IOCP

2.3IOCP

         采用Overlapped I/O的一个异步I/O框架,它的优势是操作大量句柄时效率更高;
         IOCP允许多个socket绑定到一个完成端口(可理解为比较特别的句柄)上,同时,在线程池的工作线程中监听完成端口,以此来获得这些文件句柄的IO操作结果;
注意,这里不是获取IO操作的事件,而是结果,典型的异步I/O模式;
    IOCP有这样一些特性:
    1.一个完成端口可绑定多个文件句柄;
    2.多个线程同时访问一个完成端口,没有线程安全问题;
    3.一个线程最多只能关联一个完成端口,GetQueuedCompletionStatus调用时进行绑定,线程运行时可以换绑,绑定结束条件:换绑、线程退出、完成端口被释放;
   4.完成端口创建时,有最大关联线程数,超过这个最大值的线程,会被block,直至关联线程数减少到最大值以下;
    5.完成端口关联线程数量的取值可参考CPU核数,如果线程中处理数据的流程比较长,则应加大这个值;
    6.异步IO完成前,线程等待的是完成端口,而不是某个文件句柄的异步IO操作;
    7.一个异步IO完成时,系统会把完成数据(Complete Packet)放入一个FIFO队列;
    8.一个异步IO完成时,最后一个wait的线程会被释放(LIFO),这样可以减少线程切换;
    9.可以通过PostQueuedCompletionStatus发送完成数据包,这样可以做一些自己的扩展;
    10.IOCP在线程逻辑简单,同时异步IO并发量较大时,效率更高;

3.多路复用模型(Multiplexing

         多路复用技术可以用来提高网络I/O的使用效率,是所有大中型网络框架都必须使用的技术手段,I/O复用实现、异步I/O实现都可以实现多路复用模型;
         ACE提出了两种经典的多路复用模型:ProactorReactor

3.1Reactor

Reactor一般用I/O复用实现,其工作流程如下图:


    1.注册读就绪事件和相应的事件处理器;
    2.事件分离器等待事件;
    3.事件到来,激活分离器,分离器调用事件对应的处理器;
    4.事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权。
 3.2Proactor
Proactor一般用异步I/O实现;与reactor不同的是,处理器不关心I/O就绪事件,它关注的是完成事件;
下图是Proactor的流程:
1.处理器发起异步操作,并关注I/O完成事件;
2.事件分离器等待操作完成事件;
3.分离器等待过程中,内核并行执行实际的I/O操作,并将结果数据存入用户自定义缓冲区,最后通知事件分离器读操作完成;
4.I/O完成后,通过事件分离器呼唤处理器;
5.事件处理器处理用户自定义缓冲区中的数据;

4.线程(进程)模型

4.1单线程模型

单个线程采用多路复用技术管理所有socketsocket全部设置non-blocking模式,由事件通知触发网络读写;在处理大量连接时,是非常经典的线程模型之一;
这种模型工作方式如下图所示:

需要注意的是,由于所有的socket操作都在一个线程中完成,所以必须保证在线程中,除了网络I/O操作以外,没有其他引发阻塞的调用。 

4.2多线程模型

4.2.1每个连接对应一个线程

一个网络socket对应一个处理线程,socket采用阻塞I/O模型;
这种模型是小程序和java常用的策略,对于交互式的长连接应用也是常见的选择(比如BBS),也常用来做内部服务器交互的模型。 这种策略很难满足高性能程序的需求,好处是实现极其简单,容易嵌入复杂的交互逻辑。Apacheftpd等都是这种工作模式。

4.2.2线程池

线程池一般有两种模式:Half-Sync/Half-Async模式和Leader/Followers模式

4.2.2.1半同步、半异步模式(Half-Sync/Half-Async

         这种模式有三部分组成:异步事件接收层、事件同步队列、同步事件处理层;
         其中,异步事件接收层为一个线程,同步事件处理层可以有多个线程;
         它的工作流程很清晰:
    1.异步线程负责检查网络的异步事件;
    2.发生网络事件时,异步线程把网络事件放入事件队列;
    3.同步线程从队列中获取网络事件,并执行同步的读或写操作;


这个过程需要注意的是,不要引起两个同步线程同时接收或发送一个socket的情况。

4.2.2.2领导者、追随者模式(Leader/Followers

         这种模式与Half-Sync/Half-Async完全不同,没有事件队列,没有固定的事件接收者,每个线程都是事件接收者,也是处理者;
         Leader/Followers的流程:
    1.准备若干个线程用来处理大量的事件;
    2.有一个线程作为Leader,等待事件的发生;其他的线程作为Follower,仅仅是睡眠;
    3.有事件需要处理时,如果Leader能很快处理掉,Leader会再次进入等待状态;
    4.如果Leader不能马上处理完,Leader则从Followers中指定一个新的Leader,自己去处理事件,不再当Leader;
    5.被唤醒的Follower作为新的Leader等待事件的发生;
    6. 处理事件的线程处理完毕以后,就会成为Follower的一员,直到被唤醒成为Leader;
IOCP就是典型的L/F的工作模式,当线程1GetQueuedCompletionStatus这里返回后,如果线程1的处理过程没有超过某个时间段,而是很快就返回,之后继续GetQueuedCompletionStatus,那OS会让新到的数据从线程1GetQueuedCompletionStatus获取,这样就减少了线程的CONTEXT切换代码;反之,如果线程1处理时间比较长,那么新到的数据将会由线程2GetQueuedCompletionStatus获得;

 
 

5.多进程模型

         一个客户端对应一个进程来处理,也是一种历史悠久的网络模型,linux的典型例子就是inetd服务。这种方式用来处理间断性内部数据处理时,比其常驻内存的stand-alone模式更节省系统资源。

6.成熟的IO框架介绍

ACE

“重量级的C++ I/O框架,用面向对象实现了一些I/O策略和其它有用的东西,特别是它的Reactor是用OO方式处理非阻塞I/O,而Proactor是用OO方式处理异步I/O( In particular, his Reactor is an OO way of doing nonblocking I/O, and Proactor is an OO way of doing asynchronous I/O).
从很多实际使用来看,ACE是一个很值得学习的网络框架,但由于它过于重量级,导致使用起来并不方便。
ACE中提出了两种网络模式:ProactorReactor

ASIO

C++I/O框架,逐渐成为Boost库的一部分。it’s like ACE updated for the STL era。”
支持selectepollIOCPIO模型;

libevent

Niels ProvosC编写的一个轻量级的I/O框架。它支持kqueueselectpollepoll
1.4.11版还不支持windowsIOCP,但已经有很多开发者自己修改源码,把IOCP合并进去。

阅读(3205) | 评论(0) | 转发(0) |
0

上一篇:stdext::hash_map

下一篇:ACE与设计模式

给主人留下些什么吧!~~