【IO模式】Proactor与Reactor模式|同步IO和异步IO|阻塞IO和非阻塞IO

目录

1 操作系统 IO 相关概念

1.1 内核态 / 用户态

1.2 用户空间、内核空间

1.3 IO 模型

2 Linux 系统 I/O 模型

2.1 关于同步,阻塞的解释

2.2 Linux 的几种 I/O 模型

2.2.1 同步阻塞IO (等待)

2.2.2 同步非阻塞 (需不断轮询)

2.2.3 IO多路复用 (用户不断轮询select/epoll状态,有数据则处理)

2.2.4 异步IO (用户调用后走掉,系统发现数据后通过状态、通知、回调通知调用者)

3 Java I/O 模型

4 Reactor 和Proactor

1)Reactor 和Proactor的区别

2)Reactor 模式(通知原来的线程,原来的线程执行处理函数)

3)Proactor 模式 (内核独立线程执行处理函数(回调))

reactor和proactor两种模式的个人理解


1 操作系统 IO 相关概念

1.1 内核态 / 用户态

为了限制不同的程序之间的访问能力,防止他们获取别的程序的内存数据,或者随意访问外围设备,CPU 为指令划分了访问等级。而在操作系统中,这将分为内核态和用户态两个等级

  内核态:CPU可以访问内存所有数据,包括外围设备,例如硬盘,网卡。CPU 也可以将自己从一个程序切换到另一个程序

  用户态:只能受限的访问内存,且不允许访问外围设备。占用CPU的能力被剥夺,CPU 可以被抢占

程序从内核态转换为用户态,或从用户态转换为内核态都需要一定的 CPU 资源。

1.2 用户空间、内核空间

  同内核态 / 用户态一样,内存虚拟地址空间也被分为两部分,一部分由内核使用,一部分由用户进程使用。

1.3 IO 模型

  读操作分为内核准备数据将数据从内核拷贝到用户空间两个阶段,如图

【IO模式】Proactor与Reactor模式|同步IO和异步IO|阻塞IO和非阻塞IO_第1张图片

  同样,写操作也分为从用户空间拷贝数据到内核,再从内核写数据到设备两个阶段

2 Linux 系统 I/O 模型

2.1 关于同步,阻塞的解释

陈硕:“

在处理 IO 的时候,阻塞和非阻塞都是同步 IO。
只有使用了特殊的 API 才是异步 IO。
链接:https://www.zhihu.com/question/19732473/answer/26091478

【IO模式】Proactor与Reactor模式|同步IO和异步IO|阻塞IO和非阻塞IO_第2张图片

  同步和异步:描述的是用户线程内核交互方式  (是否有回调?)

  •   同步是指用户线程发起 IO 请求后需要等待或者轮询内核 IO 操作完成后才能继续执行;(epoll的epoll_wait())
  •   异步是指用户线程发起 IO 请求后继续执行自身指令(不等待),当内核 IO 操作完成后会通知用户线程,或者调用用户线程注册的回调函数。---使用异步API, (reactor?)


  阻塞和非阻塞:描述的是用户线程调用内核 IO 操作的方式    (read、write?)

  •   阻塞是指 IO 操作需要彻底完成后才返回到用户空间。 
  •   而非阻塞是指 IO 操作被调用后立即返回给用户一个状态值,无需等到 IO 操作彻底完成。(比如调用read读socket的fd,有数据就读取返回读到的数据长度,没数据就返回-1 errno = EAGAIN)

如何区分同步异步、阻塞非阻塞

IO分两阶段(一旦拿到数据后就变成了数据操作,不再是IO):

1.数据准备阶段

2.内核空间复制数据到用户进程缓冲区(用户空间)阶段

(设备)数据准备--->内核copy to 用户空间--->程序

【IO模式】Proactor与Reactor模式|同步IO和异步IO|阻塞IO和非阻塞IO_第3张图片

     同步 IO 和异步 IO 的区别就在于第二个步骤是否阻塞,如果实际的 IO 读写阻塞请求进程,那么就是同步 IO。(数据从内核拷贝到用户空间过程中,线程是否阻塞?)
  阻塞 IO 和非阻塞 IO 的区别在于第一步,发起 IO 请求是否会被阻塞,如果阻塞直到完成那么就是传统的阻塞 IO,如果不阻塞,那么就是非阻塞 IO。(数据等待的过程中,线程是否等待?)

(阻塞IO和非阻塞IO的区别在于第一步发起IO请求是否会被阻塞:

如果阻塞直到完成那么就是传统的阻塞IO,如果不阻塞,那么就是非阻塞IO。

一般来讲: 阻塞IO模型、非阻塞IO模型、IO复用模型(select/poll/epoll)、信号驱动IO模型都属于同步IO,因为阶段2是阻塞的(尽管时间很短)。

同步IO和异步IO的区别就在于第二个步骤是否阻塞:

如果不阻塞,而是操作系统帮你做完IO操作再将结果返回给你,那么就是异步IO)

【IO模式】Proactor与Reactor模式|同步IO和异步IO|阻塞IO和非阻塞IO_第4张图片

更详细:https://www.cnblogs.com/loveer/p/11479249.html

2.2 Linux 的几种 I/O 模型

一、同步、异步 x 阻塞、非阻塞

  1. 同步阻塞IO(Blocking IO)              ----最原始
  2. 同步非阻塞IO(Non-blocking IO)   ----epoll
  3. IO多路复用(IO Multiplexing)         ---- (同步
  4. 异步IO                                               ----libevent

(阻塞与否 是自身,异步与否是与外部协作的关系)

二、

  1. 信号驱动式IO(很少用,不解释)

阻塞IO:当应用层发起read请求时,发出read请求的线程会被挂起,直到操作系统完成数据从内核区到应用层缓冲区的拷贝,read调用才返回;write操作也是一样。

【IO模式】Proactor与Reactor模式|同步IO和异步IO|阻塞IO和非阻塞IO_第5张图片

非阻塞IO:当应用层发起read请求,而操作系统没有准备好数据,read调用会立刻返回;同时操作系统会返回一个EWOULDBLOCK的错误码。但发起read请求的线程会不断地去询问操作系统“数据准备好了吗?”,如果准备好了,那么就把数据从内核空间拷贝到应用层缓冲区。
【IO模式】Proactor与Reactor模式|同步IO和异步IO|阻塞IO和非阻塞IO_第6张图片

上面就是阻塞IO和非阻塞IO,但是有一点需要说明,在操作系统把数据准备好,并把数据拷贝出来的过程是同步的,也就是说必须要拷贝完,read才会返回。所以同步/异步是用于形容数据从内核空间拷贝到应用程序缓冲区的过程。
同步:当操作系统内核准备好数据,应用程序发出read请求,数据的拷贝是和read函数同步进行的,如果数据较多,那么read调用的耗时会较长。
异步:当应用程序发出***aio_read***请求后,无论操作系统是否把数据准备好都会立即返回,操作系统自主把数据从内核空间拷贝到应用程序的缓冲区来,而后通知应用程序,数据已经拷贝完成;无需应用程序等待或者以轮询的方式去查看数据是否拷贝完成。
【IO模式】Proactor与Reactor模式|同步IO和异步IO|阻塞IO和非阻塞IO_第7张图片

阻塞IO:去书店问老板要具体某一本书,老板翻箱倒柜地寻找,你就一直在那里等,直到老板找到为止。
非阻塞IO:你觉得第一种方式太笨了,去书店问老板要某本书,老板拼命地找,你可以去喝喝咖啡或者上街看看美女,过一阵子再问老板:“老板,书找到了吗?”。
非阻塞IO+多路复用技术:后来觉得总是去问老板“书有没有找到”太费事了,你叫老板通知你,书找到了,你就去拿。老板通知你这件事就相当于操作系统的IO复用技术(select、poll、epoll)。
上述三种情况中,我们去拿书的时候便是***同步***的,也就是老板给我们书,肯定是一次性给完,不可能给一本,你出去一下,再回来取第二本。
异步:相当于现在的网购,在网上写好地址,写好书名,付好钱,老板那里有书了,直接寄给你,等你收到通知时,书已经邮寄到你家里了。
windows中IOCP模型就是异步思想的最好体现,操作系统帮我们完成数据的收发,我们只需对数据进行加工便可;目前我所在公司开发的摄像头组件服务器便是采用IOCP模型,同时接收几千台设备上报的报警信息都没问题。
那么现在我们应该清楚了,Linux平台下,无论是采用哪种IO复用技术都是同步调用,也就是***IO复用机制+同步调用+非阻塞***模型。或者***IO复用机制+同步调用+阻塞***模型。没有其他的组合,在Linux平台下,基于socket的异步IO调用,在性能上还是有待考究的,目前只实现了磁盘的异步IO。

https://my.oschina.net/u/4264465/blog/4503325

介绍完这些基本概念,现在逐步剥开Reactor和proactor模式的面纱。

Reactor模式:在Linux平台下,基于epoll模型的事件通知机制,当socket上有读写事件发生时,便通知应用程序发出read/write操作,其他时间应用程序可以去做其他事情,这种模型便是Reactor模式。换句话说,Reactor模式是基于待完成的socket IO事件,需要应用程序自己去处理的。
Proactor模型:采用IO多路复用技术,比如:IOCP模型。唯一不同的是应用程序无需发出read/write请求,当得到事件通知时,操作系统已经把数据读取到应用层缓冲区或者将应用层数据拷贝内核空间,完成数据的发送。应用程序只需准备装数据的容器和待发送的数据。那么Proactor模式也就可以理解为:IO复用机制+异步调用,这里就没有所谓的阻塞和非阻塞的概念了。

https://my.oschina.net/u/4264465/blog/4503325

2.2.1 同步阻塞IO (等待)

  最简单的 IO 模型,用户线程在读写时被阻塞

  应用程序为了执行读写操作,会调用相应的一个系统调用,将系统控制权交给内核(用户态到内核态切换),然后就进行等待(就是被阻塞)。用户线程这时不能做任何事情。

  内核开始执行这个系统调用,执行完毕后复制数据到用户态内存,向用户线程返回响应。用户线程得到响应后,就不再阻塞,并进行后面的工作。

2.2.2 同步非阻塞 (需不断轮询)

  在同步阻塞的基础上,用户线程读写时,不被阻塞,而是立即返回之后用户线程不断发起IO请求。

  数据未到达时系统返回一状态值,数据到达后才真正读取数据,为了拿到数据,需不断轮询,无谓地消耗了大量的CPU

  一般很少直接使用这种模型,而是在其他 IO 模型中使用非阻塞 IO 这一特性

2.2.3 IO多路复用 (用户不断轮询select/epoll状态,有数据则处理)

  多路复用有一个选择器(Selector),将需要处理的 Channel (其实就是 socket)注册到选择器上。

  然后轮询 Selector(调用 select 并阻塞线程)。当有 Channel 准备完成,可以进行操作(读,写,连接)时,select 会返回。

  这时用户线程需要迭代所有 SelectionKey(对应 Channel 的网络事件) 并做相应的业务处理。

  另一种多路复用是 epoll,将 socket 注册到 epool(由 epoll_create创建),将注册需要处理的网络事件。

  然后轮询(调用epoll_wait,阻塞线程)。当可以进行操作时,epoll_wait 返回,且返回所有准备好的网络事件,用户线程只需要处理已经准备好的网络事件。

  当数十万并发连接存在时,epoll 和 select 的区别比较明显。在这种情况下,可能每一毫秒只有数百个活跃的连接,

  同时其余数十万连接在这一毫秒是非活跃的,Selector 将会浪费大量时间在查询哪些非活跃的连接上。而 epoll 可以节省时间。

  另外,每次调用 select,需要把 fd 集合从用户态拷贝到内核态,而 select 支持的文件描述符数量太小

  从单个连接的处理流程看,多路复用有点像同步阻塞 IO,但多路复用使得用户可以在一个线程内同时处理多个 socket 的 IO 请求。

2.2.4 异步IO (用户调用后走掉,系统发现数据后通过状态、通知、回调通知调用者)

  调用发出后,系统立刻返回,实际处理这个调用的函数在完成后,通过状态、通知和回调来通知调用者的输入输出操作。异步IO的工作机制是:告知内核启动某个操作,并让内核在整个操作完成后通知

3 Java I/O 模型

  jdk 提供了几种 I/O 模型

  1.   bio,采用同步阻塞模型,JDK1.4开始支持
  2.   nio,采用多路复用模型,JDK1.4开始支持
  3.   aio,采用异步 IO 模型,JDK1.7开始支持

4 Reactor 和Proactor

1)Reactor 和Proactor的区别

Proactor(前摄式--前面代理)

Reactor (反应器--反应,通知一下)

reactor:能收了你跟俺说一声。
proactor: 你给我收十个字节,收好了跟俺说一声。

ractor:反应器,有数据来了你反应给我,我去读

proactor:代理人,有数据来了你代理我读好,然后再通知我

Reactor:非阻塞同步网络模型,可以理解为:来了事件我通知你,你来处理

Proactor:异步网络模型,可以理解为:来了事件我来处理,处理完了我通知你

家里有两个佣人, 一个叫reactor, 另一个叫proactor。

1.reactor外卖到会提醒你提醒你外卖到了,你去处理外卖

2.proactor外卖到了,帮你把外卖拿到饭桌上

ps:epoll 和reactor的关系是什么?

reactor是对epoll的一层封装,epoll是对io进行管理,reactor将对io的管理转化为对事件的管理

2)Reactor 模式(通知原来的线程,原来的线程执行处理函数)

  reactor 模式建立在多路复用的基础上,这个模式有两个主要角色:事件分离器(dispatcher),事件处理器(handler)

    事件分离器:处理读写,连接等行为,通过由线程池实现

    事件处理器:同一个线程实现,实现事件循环,不断调用多路分离函数 select (或 epoll_wait)。当有某个事件被激活时,调用关联的事件处理器处理

  reactor 处理流程如下

【IO模式】Proactor与Reactor模式|同步IO和异步IO|阻塞IO和非阻塞IO_第8张图片

 

 

3)Proactor 模式 (内核独立线程执行处理函数(回调))

  proactor 模式建立在异步 IO 上。

  用户线程将AsynchronousOperation(读/写等)、Proactor 以及操作完成时的 CompletionHandler 注册到 AsynchronousOperationProcessor。

  当用户线程调用异步API后,用户线程继续执行自己的任务,AsynchronousOperationProcessor 会开启独立的内核线程执行异步操作,实现真正的异步读写。

  当异步 IO操作完成时,将调用 CompletionHandler

  处理流程如下:

【IO模式】Proactor与Reactor模式|同步IO和异步IO|阻塞IO和非阻塞IO_第9张图片

图片摘自 http://www.cnblogs.com/fanzhidongyzby/p/4098546.html

对比两者的区别https://blog.csdn.net/wanbf123/article/details/78062802

主动和被动

以主动写为例: 


Reactor将handle放到select(),等待可写就绪,然后调用write()写入数据;写完处理后续逻辑; 
Proactor调用aoi_write后立刻返回,由内核负责写操作,写完后调用相应的回调函数处理后续逻辑;

可以看出,Reactor被动的等待指示事件的到来并做出反应;它有一个等待的过程,做什么都要先放入到监听事件集合中等待handler可用时再进行操作; 
Proactor直接调用异步读写操作,调用完后立刻返回;

实现

Reactor实现了一个被动的事件分离和分发模型,服务等待请求事件的到来,再通过不间断的同步处理事件,从而做出反应

Proactor实现了一个主动的事件分离和分发模型;这种设计允许多个任务并发的执行,从而提高吞吐量;并可执行耗时长的任务(各个任务间互不影响)

【IO模式】Proactor与Reactor模式|同步IO和异步IO|阻塞IO和非阻塞IO_第10张图片

优点

Reactor实现相对简单,对于耗时短的处理场景处理高效; 
操作系统可以在多个事件源上等待,并且避免了多线程编程相关的性能开销和编程复杂性; 事件的串行化对应用是透明的,可以顺序的同步执行而不需要加锁; 事务分离:将与应用无关的多路分解和分配机制和与应用相关的回调函数分离开来,

Proactor性能更高,能够处理耗时长的并发场景;

缺点

Reactor处理耗时长的操作会造成事件分发的阻塞,影响到后续事件的处理;

Proactor实现逻辑复杂;依赖操作系统对异步的支持目前实现了纯异步操作的操作系统少,实现优秀的如windows IOCP,但由于其windows系统用于服务器的局限性,目前应用范围较小;而Unix/Linux系统对纯异步的支持有限,应用事件驱动的主流还是通过select/epoll来实现;


适用场景

Reactor:同时接收多个服务请求,并且依次同步的处理它们的事件驱动程序; 
Proactor:异步接收和同时处理多个服务请求的事件驱动程序;
---------------------

 参考:https://www.jianshu.com/p/bae386af45ca 《Reactor 与Proactor》

https://www.cnblogs.com/randomsort/p/9618365.html 《Java I/O 模型 》

https://blog.csdn.net/wanbf123/article/details/78062802  《Reactor和Proactor对比以及优缺点(netty的底层原理reactor模型)》

https://www.cnblogs.com/bitkevin/p/5724410.html

reactor和proactor两种模式的个人理解

其实要理解这两种模式很多人会说这很简单,无非一种是同步非阻塞行(nio),一种是异步非阻塞行(aio),

同步、异步、阻塞、非阻塞定义见2.1

在给上两张图来说明一下,不过不知道大家是否都能理解这个图(当然这个图也是我从网上截下来的),不明白的可以共同探讨

【IO模式】Proactor与Reactor模式|同步IO和异步IO|阻塞IO和非阻塞IO_第11张图片

【IO模式】Proactor与Reactor模式|同步IO和异步IO|阻塞IO和非阻塞IO_第12张图片

你可能感兴趣的:(内核,网络,epoll,linux,java)