【NIO系列】——之IO模型

这是【NIO系列】第二篇,欢迎持续关注:java

【NIO系列】——之TCP探秘linux

上一篇咱们讲到了关于TCP/IP协议的一些内容,这些是网络编程的必备知识。在了解NIO以前咱们必需要了解一下对应的系统层IO模型,好比java的NIO对应是那种IO模型,阻塞和同步的差别在哪里,又是否相同。了解了这些更方便咱们的后续的NIO探解。编程

1、同步、异步、阻塞、非阻塞

同步、异步,阻塞、非阻塞,这四种状态常有人分不清,主要是这四种状态的定义自己也不是很明确,因此各类解答的方式都有。常见的分类有如下:网络

  1. 同步阻塞IO架构

  2. 同步非阻塞IO异步

  3. 异步非阻塞IOsocket

针对某种IO模型,咱们如何分类,能够基于POSIX对同步/异步的定义来判别:async

- A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes;函数

- An asynchronous I/O operation does not cause the requesting process to be blocked;spa

那么从上咱们能够看出:

阻塞:是否阻塞主要体如今调用的线程是否能够干别的,关注的是程序的等待状态

同步:是否同步体如今消息通讯机制上 。

也就是说同步和异步说的是消息的通知机制,阻塞非阻塞说的是线程的状态 。

 

若是说以上的定义依然没法判别,咱们能够从输入操做的两个阶段来看:

通常来讲,一个输入操做一般包括两个不一样阶段:

(1)等待数据准备好;(2)从内核向进程复制数据。

是否同步的判断依据是:是否针对的是整个过程,也就是2个阶段,是否有阻塞。

是否阻塞的判断依据是:按程序(线程)等待消息通知时的状态角度来讲的,也就是主要是针对第一阶段来讲。

 

举例

咱们举例来讲:

好比说作饭这件事,通常要分为连个步骤。

一、买菜,准备食材

二、炒菜,作出饭菜

 

方案一:本身动手处理。

一、去超市买菜,准备食材(阻塞,当前时段只能作一件事,且须要持续的等待)

二、回家切菜,炒菜,作出美味饭菜(阻塞,仍是本身来处理)

评价: 方案一同步阻塞。首先阶段一是阻塞的,因此认定为阻塞,两个阶段都是阻塞的,认定为同步的。

 

方案二:盒马配送食材,本身作饭

一、网上下单,盒马配送食材,快递到了会敲门联系你。(非阻塞的,这期间你能够干其余事)

二、拿到菜,切菜、炒菜,作出美味饭菜(阻塞)

评价:方案二为同步非阻塞。阶段一为非阻塞,认定为非阻塞。阶段二为阻塞,两阶段中有一个为阻塞,认定为同步。

 

方案三:盒马配送,请阿姨作饭

一、网上下单,盒马配送食材,快递到了会敲门联系你。(非阻塞的,这期间你能够干其余事)

二、网上请阿姨小时工,帮忙作这一餐,作好通知我。(非阻塞,期间能够干其余事)

评价:方案三为异步非阻塞。阶段一为非阻塞,认定为非阻塞。阶段二非阻塞,则两阶段中都没有阻塞,认定为异步。

那么是否有异步阻塞IO模型,没有,要记得异步状态是包含二个阶段的,若是有阻塞的过程,为什么还叫异步?

网上有不少介绍有异步阻塞模型的,我目前查到的资料尚未这个证实,如有找到相关论文,还请指教。目前我认为没有这个模型的。

 

2、Unix 5种I/O模型

《UNIX网络编程:卷一》的第六章书中列出了五种IO模型:

  • 阻塞式I/O;

  • 非阻塞式I/O;

  • I/O复用(select,poll,epoll...);

  • 信号驱动式I/O(SIGIO);

  • 异步I/O(POSIX的aio_系列函数);

1.阻塞式I/O

同步阻塞 IO 模型是最经常使用的一个模型,也是最简单的模型。在linux中,默认状况下全部的socket都是blocking。它符合人们最多见的思考逻辑。

在这个IO模型中,用户空间的应用程序执行一个系统调用(recvform),这会致使应用程序阻塞,什么也不干,直到数据准备好,等待kernel准备好从网络上接收到的数据报 + 等待收到的报文被从kernel复制到buf中,recvfrom方法才会返回,最后进程再处理数据。

这就是阻塞式IO模型

 

2.非阻塞式I/O

非阻塞IO时对一个非阻塞描述符循环调用recvfrom,持续的轮询(polling),以查看某个操做是否就绪。与阻塞IO不同,"非阻塞将大的整片时间的阻塞分红N多的小的阻塞, 因此进程不断地有机会 '被' CPU光顾"。

非阻塞的recvform系统调用调用以后,进程并无被阻塞,内核立刻返回给进程,若是数据还没准备好,此时会返回一个error。进程在返回以后,能够干点别的事情,而后再发起recvform系统调用。如此循环的进行recvform系统调用,检查内核数据,直到数据准备好,再拷贝数据到进程。拷贝数据整个过程,进程仍然是属于阻塞的状态

这就是非阻塞式IO模型

3.I/O复用

IO multiplexing就是咱们说的select,poll,epoll 。为什么叫多路复用,是由于它I/O多路复用能够同时监听多个fd,如此就减小了为每一个须要监听的fd开启线程的开销。

select调用是内核级别的,能够等待多个socket,能实现同时对多个IO端口进行监听,当其中任何一个socket的数据准好了,就能返回进行可读而后进程再进行recvform系统调用,将数据由内核拷贝到用户进程,这个过程是阻塞的。

I/O复用模型会用到select、poll、epoll函数,这几个函数也会使进程阻塞,可是和阻塞I/O所不一样的的,这几个函数能够同时阻塞多个I/O操做`。并且能够同时对多个读操做,多个写操做的I/O函数进行检测,直到有数据可读或可写时(不是等到socket数据所有到达再处理, 而是有了一部分数据就会调用用户进程来处理),才真正调用I/O操做函数。

IO复用有人把其成为同步非阻塞的,也有称为同步阻塞。其实这个是否阻塞还须要看第一个阶段,第一个阶段有的阻塞,有的不阻塞。主要也是阻塞在select阶段,属于用户主动等待阶段,咱们且规范为阻塞状态,因此,把IO多路复用归为同步阻塞模式

这是IO复用的模型:

 

select、poll、epoll的不一样

4.信号驱动式I/O

信号驱动式I/O:首先咱们容许Socket进行信号驱动IO,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,能够在信号处理函数中调用I/O操做函数处理数据。

也就是说第一个阶段,彻底是非阻塞的,等数据到达会给一个信号通知,第二个阶段recvfrom仍是阻塞过程,和之上无差别。

信号驱动式I/O 过程以下:

5.异步I/O

异步IO不是顺序执行,用户进程进行aio_read系统调用以后,不管内核数据是否准备好,都会直接返回给用户进程,而后用户态进程能够去作别的事情。等到socket数据准备好了,内核直接复制数据给进程,而后从内核向进程发送通知IO两个阶段,进程都是非阻塞的

 

 

总结

针对这5中IO模型,我采用一张图来总结一下。

 

3、java IO

Unix中的五种I/O模型,除信号驱动I/O外,Java对其它四种I/O模型都有所支持。其中Java最先提供的blocking I/O便是同步阻塞I/O,而NIO便是同步非阻塞I/O,同时经过NIO实现的Reactor模式便是I/O复用模型的实现,经过AIO实现的Proactor模式便是异步I/O模型的实现。

因此说严格意义上来讲,经过Reactor模式实现的NIO,和unix中的I/O多路复用是相同的概念,但这是一种编程模型,而不是原生支持。这也是咱们下面所要进行的netty讲解的主要思想。

 

更多架构知识,欢迎关注个人公众号,大码候(cool_wier)