【网络】再谈select, iocp, epoll,kqueue及各种I/O复用机制 && Reactor与Proactor的概念

摘要:
首先,应用程序调用recvfrom()以传输到内核。注意,内核有两个进程,waitfordata和copydatafromcernelluser。Select和iocp分别对应于第三个和第五个模型。epoll和kqueue呢?这里我们要讨论两种设计模式,Reactor和Proactor。事实上,它是各种I/O重用机制的封装。然而,低效率也是相对的。根据情况,也可以通过良好的设计来改进epoll。kqueue是Reactor模式,IOCP是Proactor模式。与事件分隔符相关的两种模式是Reactors和Proactor。Reactor模式采用同步IO,而Proactor采用异步IO。例如,以读操作为例,这将有助于理解Reactors和Proactor之间的区别。

首先,介绍几种常见的I/O模型及其区别,如下:

  • blocking I/O
  • nonblocking I/O
  • I/O multiplexing (select and poll)
  • signal driven I/O (SIGIO)
  • asynchronous I/O (the POSIX aio_functions)

blocking I/O
这个不用多解释吧,阻塞套接字。下图是它调用过程的图示:

a1

重点解释下上图,下面例子都会讲到。首先application调用 recvfrom()转入kernel,注意kernel有2个过程,wait for data和copy data from kernel to user。直到最后copy complete后,recvfrom()才返回。此过程一直是阻塞的。

nonblocking I/O:
与blocking I/O对立的,非阻塞套接字,调用过程图如下:

a2

可以看见,如果直接操作它,那就是个轮询。。直到内核缓冲区有数据。

I/O multiplexing (select and poll)
最常见的I/O复用模型,select。

a3

select先阻塞,有活动套接字才返回。与blocking I/O相比,select会有两次系统调用,但是select能处理多个套接字。

signal driven I/O (SIGIO)
只有UNIX系统支持,感兴趣的课查阅相关资料

a4

I/O multiplexing (select and poll)相比,它的优势是,免去了select的阻塞与轮询,当有活跃套接字时,由注册的handler处理。

asynchronous I/O (the POSIX aio_functions)
很少有*nix系统支持,windows的IOCP则是此模型

a5

完全异步的I/O复用机制,因为纵观上面其它四种模型,至少都会在由kernel copy data to appliction时阻塞。而该模型是当copy完成后才通知application,可见是纯异步的。好像只有windows的完成端口是这个模型,效率也很出色。

下面是以上五种模型的比较

a6

可以看出,越往后,阻塞越少,理论上效率也是最优。

=====================分割线==================================

5种模型的比较比较清晰了,剩下的就是把select,epoll,iocp,kqueue按号入座那就OK了。

select和iocp分别对应第3种与第5种模型,那么epoll与kqueue呢?其实也于select属于同一种模型,只是更高级一些,可以看作有了第4种模型的某些特性,如callback机制。

那么,为什么epoll,kqueue比select高级?

答案是,他们无轮询。因为他们用callback取代了。想想看,当套接字比较多的时候,每次select()都要通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍。这会浪费很多CPU时间。如果能给套接字注册某个回调函数,当他们活跃时,自动完成相关操作,那就避免了轮询,这正是epoll与kqueue做的。

windows or *nix (IOCP or kqueue/epoll)?

诚然,Windows的IOCP非常出色,目前很少有支持asynchronous I/O的系统,但是由于其系统本身的局限性,大型服务器还是在UNIX下。而且正如上面所述,kqueue/epoll 与 IOCP相比,就是多了一层从内核copy数据到应用层的阻塞,从而不能算作asynchronous I/O类。但是,这层小小的阻塞无足轻重,kqueue与epoll已经做得很优秀了。

提供一致的接口,IO Design Patterns

实际上,不管是哪种模型,都可以抽象一层出来,提供一致的接口,广为人知的有ACE,Libevent这些,他们都是跨平台的,而且他们自动选择最优的I/O复用机制,用户只需调用接口即可。说到这里又得说说2个设计模式,Reactor and Proactor。有一篇经典文章http://www.artima.com/articles/io_design_patterns.html值得阅读,Libevent是Reactor模型,ACE提供Proactor模型。实际都是对各种I/O复用机制的封装。

Java nio包是什么I/O机制?

我曾天真的认为java nio封装的是IOCP。。现在可以确定,目前的java本质是select()模型,可以检查/jre/bin/nio.dll得知。至于java服务器为什么效率还不错。。我也不得而知,可能是设计得比较好吧。。-_-。

=====================分割线==================================

总结一些重点:

    1. 只有IOCP是asynchronous I/O,其他机制或多或少都会有一点阻塞。
    2. select低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善
    3. epoll, kqueue是Reacor模式,IOCP是Proactor模式。
    4. java nio包是select模型。。
1、标准定义

两种I/O多路复用模式:Reactor和Proactor

一般地,I/O多路复用机制都依赖于一个事件多路分离器(Event Demultiplexer)。分离器对象可将来自事件源的I/O事件分离出来,并分发到对应的read/write事件处理器(Event Handler)。开发人员预先注册需要处理的事件及其事件处理器(或回调函数);事件分离器负责将请求事件传递给事件处理器。两个与事件分离器有关的模式是Reactor和Proactor。Reactor模式采用同步IO,而Proactor采用异步IO。

在Reactor中,事件分离器负责等待文件描述符或socket为读写操作准备就绪,然后将就绪事件传递给对应的处理器,最后由处理器负责完成实际的读写工作。


而在Proactor模式中,处理器--或者兼任处理器的事件分离器,只负责发起异步读写操作。IO操作本身由操作系统来完成。传递给操作系统的参数需要包括用户定义的数据缓冲区地址和数据大小,操作系统才能从中得到写出操作所需数据,或写入从socket读到的数据。事件分离器捕获IO操作完成事件,然后将事件传递给对应处理器。比如,在windows上,处理器发起一个异步IO操作,再由事件分离器等待IOCompletion事件。典型的异步模式实现,都建立在操作系统支持异步API的基础之上,我们将这种实现称为“系统级”异步或“真”异步,因为应用程序完全依赖操作系统执行真正的IO工作。


举个例子,将有助于理解Reactor与Proactor二者的差异,以读操作为例(类操作类似)。
在Reactor中实现读:

- 注册读就绪事件和相应的事件处理器
- 事件分离器等待事件
- 事件到来,激活分离器,分离器调用事件对应的处理器。
- 事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权。
在Proactor中实现读:

- 处理器发起异步读操作(注意:操作系统必须支持异步IO)。在这种情况下,处理器无视IO就绪事件,它关注的是完成事件。
- 事件分离器等待操作完成事件
- 在分离器等待过程中,操作系统利用并行的内核线程执行实际的读操作,并将结果数据存入用户自定义缓冲区,最后通知事件分离器读操作完成。
- 事件分离器呼唤处理器。
- 事件处理器处理用户自定义缓冲区中的数据,然后启动一个新的异步操作,并将控制权返回事件分离器。

可以看出,两个模式的相同点,都是对某个IO事件的事件通知(即告诉某个模块,这个IO操作可以进行或已经完成)。在结构上,两者也有相同点:demultiplexor负责提交IO操作(异步)、查询设备是否可操作(同步),然后当条件满足时,就回调handler;不同点在于,异步情况下(Proactor),当回调handler时,表示IO操作已经完成;同步情况下(Reactor),回调handler时,表示IO设备可以进行某个操作(can read or can write)。

2、通俗理解

使用Proactor框架和Reactor框架都可以极大的简化网络应用的开发,但它们的重点却不同。

Reactor框架中用户定义的操作是在实际操作之前调用的。比如你定义了操作是要向一个SOCKET写数据,那么当该SOCKET可以接收数据的时候,你的操作就会被调用;而Proactor框架中用户定义的操作是在实际操作之后调用的。比如你定义了一个操作要显示从SOCKET中读入的数据,那么当读操作完成以后,你的操作才会被调用。

Proactor和Reactor都是并发编程中的设计模式。在我看来,他们都是用于派发/分离IO操作事件的。这里所谓的IO事件也就是诸如read/write的IO操作。"派发/分离"就是将单独的IO事件通知到上层模块。两个模式不同的地方在于,Proactor用于异步IO,而Reactor用于同步IO。

3、备注

其实这两种模式在ACE(网络库)中都有体现;如果要了解这两种模式,可以参考ACE的源码,ACE是开源的网络框架,非常值得一学。。

参考资料:

再谈select, iocp, epoll,kqueue及各种I/O复用机制:http://blog.sae.sina.com.cn/archives/5042

Reactor与Proactor的概念:http://www.cnblogs.com/dawen/archive/2011/05/18/2050358.html

 

免责声明:文章转载自《【网络】再谈select, iocp, epoll,kqueue及各种I/O复用机制 && Reactor与Proactor的概念》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇查找2个分支的共同父节点SQL Server窗口函数:ROWS与RANGE下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

【sql进阶】SQL Server 将某一列的值拼接成字符串

今天在统计报表的时候有这么一个需求,将一列字符串拼接成一行,然后展示到新的列中。 每一项的服务列表如下: 最终想要的结果是 sql如下: select AuxTypeName + ',' from ( SELECT DISTINCT(AuxTypeName) FROM _VIPHall_Link_AuxType LEFT JOIN dbo.[_...

Mybatis

JDBCJDBC相关概念 JAVA程序都是通过JDBC连接数据库的,通过SQL对数据库编程,JDBC是由SUN公司提出的一些列规范,只定义了接口规范,具体实现由各个数据库厂商去实现,它是一种典型的桥接模式。 桥接模式是一种结构型设计模式,它的主要特点是把抽象与行为实现分离开来,分别定义接口,可以保持各部分的独立性以及应对他们的功能扩展。 JDBC规范...

数据库概述及基本操作

目录:数据库概述           SQL Sever的简介 数据库概述       数据库是相关的多维数据集及其所共享的对象的容器。这些对象包括数据源(表)、表、视图、规则、角色、存储过程、自定义函数等。如果多个多维数据集要共享这些对象,那么这些对象和多维数据集必须在同一个数据库中。简单来说,数据库就是管理数据的地方。  数据库的发展阶段:层次型、网...

Windows,Linux的select函数功能差异

Windows,Linux的select函数功能差异 感谢主,Windows当年也实现了select函数,这让我们的跨平台大业至少顺畅了一节。但由于Windows渗入骨髓的叛逆心理,他总要和UNIX的实现保持一些差别,让你无可奈何。首先是Windows的select函数的参数接口设计和Linux下有较大差别,这个在我的《设计极其糟糕的select函数》就讨...

oracle创建用户、授予权限及删除用户

创建用户 oracle对表空间 USERS 无权限 alter user 用户名 quota unlimited on users; //创建临时表空间 create temporary tablespace test_temp tempfile 'E:/oracle/product/10.2.0/oradata/testserver/test_tem...

oracle函数,查询,事务

函数包括:单行函数,多行函数(分组函数) 数值函数: --绝对值 select abs(-12.3) fromdual; --向上取值 select ceil(5.3) fromdual; --向下取值 select floor(5.3 )fromdual; --四舍五入 select round(123.4124,2)fromdual; --截取小数点...