02-IO介绍

1.IO的基本原理

用户程序进行IO的读写,依赖于底层的IO读写,基本上会用到底层的read&write两大系统调用。在不同的操作系统中,IO读写的系统调用的名称可能不完全一样,但是基本功能是一样的。

调用操作系统的read,是把数据从内核缓冲区复制到进程缓冲区;而write系统调用,是把数据从进程缓冲区复制到内核缓冲区。
在这里插入图片描述

2.几种主要的IO模型

1.同步与异步

  • 同步: 同步就是发起一个调用后,被调用者未处理完请求之前,调用不返回。
  • 异步: 异步就是发起一个调用后,立刻得到被调用者的回应表示已接收到请求,但是被调用者并没有返回结果,此时我们可以处理其他的请求,被调用者通常依靠事件,回调等机制来通知调用者其返回结果。

同步和异步的区别最大在于异步的话调用者不需要等待处理结果,被调用者会通过回调等机制来通知调用者其返回结果。

同步IO是指用户空间的线程是主动发起IO请求的一方,内核空间是被动接受方。异步IO则反过来,是指系统内核是主动发起IO请求的一方,用户空间的线程是被动接受方。

2.阻塞和非阻塞

  • 阻塞: 阻塞就是发起一个请求,调用者一直等待请求结果返回,也就是当前线程会被挂起,无法从事其他任务,只有当条件就绪才能继续。
  • 非阻塞: 非阻塞就是发起一个请求,调用者不用一直等着结果返回,可以先去干其他事情。

3.同步阻塞IO(Blocking IO)

在阻塞式IO模型中,Java应用程序从IO系统调用开始,直到系统调用返回,在这段时间内,Java进程是阻塞的。返回成功后,应用进程开始处理用户空间的缓存区数据。

在这里插入图片描述

阻塞IO的优点是:应用的程序开发非常简单;在阻塞等待数据期间,用户线程挂起。在阻塞期间,用户线程基本不会占用CPU资源。

阻塞IO的缺点是:一般情况下,会为每个连接配备一个独立的线程;反过来说,就是一个线程维护一个连接的IO操作。在并发量小的情况下,这样做没有什么问题。但是,当在高并发的应用场景下,需要大量的线程来维护大量的网络连接,内存、线程切换开销会非常巨大。因此,基本上阻塞IO模型在高并发应用场景下是不可用的。

4.同步非阻塞NIO(None Blocking IO)

在NIO模型中,应用程序一旦开始IO系统调用,会出现以下两种情况:

(1)在内核缓冲区中没有数据的情况下,系统调用会立即返回,返回一个调用失败的信息。

(2)在内核缓冲区中有数据的情况下,是阻塞的,直到数据从内核缓冲复制到用户进程缓冲。复制完成后,系统调用返回成功,应用进程开始处理用户空间的缓存数据。
在这里插入图片描述

同步非阻塞IO的优点:每次发起的IO系统调用,在内核等待数据过程中可以立即返回。用户线程不会阻塞,实时性较好。

同步非阻塞IO的缺点:不断地轮询内核,这将占用大量的CPU时间,效率低下。

5.IO多路复用模型(IO Multiplexing)

通过该系统调用,一个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是内核缓冲区可读/可写),内核能够将就绪的状态返回给应用程序。随后,应用程序根据就绪的状态,进行相应的IO系统调用。

在这里插入图片描述

IO多路复用模型的优点:与一个线程维护一个连接的阻塞IO模式相比,使用select/epoll的最大优势在于,一个选择器查询线程可以同时处理成千上万个连接(Connection)。系统不必创建大量的线程,也不必维护这些线程,从而大大减小了系统的开销。

IO多路复用模型的缺点:本质上,select/epoll系统调用是阻塞式的,属于同步IO。都需要在读写事件就绪后,由系统调用本身负责进行读写,也就是说这个读写过程是阻塞的。

6.异步IO模型(Asynchronous IO)

AIO的基本流程是:用户线程通过系统调用,向内核注册某个IO操作。内核在整个IO操作(包括数据准备、数据复制)完成后,通知用户程序,用户执行后续的业务操作。

在异步IO模型中,在整个内核的数据处理过程中,包括内核将数据从网络物理设备(网卡)读取到内核缓冲区、将内核缓冲区的数据复制到用户缓冲区,用户程序都不需要阻塞。

在这里插入图片描述

7.理解

  • 同步阻塞:安排了一份工作,一直盯着;
  • 同步非阻塞:安排了一份工作,隔一段时间问一下。收到做完和没做完的返回;
  • 多路复用:给多个人安排了工作,隔一段时间都问一下;
  • 异步:安排了一份工作,然后就不管了,别人做好后告诉自己。

Netty框架,使用的就是IO多路复用模型,而不是异步IO模型。

核心在于同步异步,阻塞非阻塞的理解

同步: 简单理解为用户主动发起请求得到最终的数据
异步: 数据就绪且到用户区,系统回调推给用户进程

阻塞: 发起请求,就绪之后才返回
非阻塞: 发起请求,立马返回

————————————————

io分两步,一请求等待数据,二数据从内核到用户区

同步阻塞: 发起请求等上面两步完成

同步非阻塞: 发起请求,没数据立马返回,轮询监测是否有数据,有,执行数据从内核到用户区的拷贝(步骤二阻塞,步骤一监测非阻塞)

io复用: 发起请求,立马返回,专门一个角色来轮询判断所有的请求中是否有数据,有,则执行数据从内核到用户区的拷贝(此过程阻塞)

异步非阻塞: 发起请求,当数据准备就绪,且拷贝到用户区之后,回调

——————————

前面三个都是同步,只有最后一个是异步

3.java NIO

从1.4版本开始,引进了新的异步IO库,被称为JavaNew IO类库,简称为JAVA NIO。New IO类库的目标,就是要让Java支持非阻塞IO。

“老的”阻塞式Java IO为OIO(Old IO)。

Java NIO由以下三个核心组件组成:

  • Channel(通道)
  • Buffer(缓冲区)
  • Selector(选择器)

1.NIO和OIO的对比

  • OIO是面向流(Stream Oriented)的,NIO是面向缓冲区(Buffer Oriented)的。
  • OIO的操作是阻塞的,而NIO的操作是非阻塞的。
  • OIO没有选择器(Selector)概念,而NIO有选择器的概念。

2.Channel(通道)

在OIO中,同一个网络连接会关联到两个流:一个输入流(Input Stream),另一个输出流(Output Stream)。通过这两个流,不断地进行输入和输出的操作。

在NIO中,同一个网络连接使用一个通道表示,所有的NIO的IO操作都是从通道开始的。一个通道类似于OIO中的两个流的结合体,既可以从通道读取,也可以向通道写入。

3.Buffer(缓冲区)

应用程序与通道(Channel)主要的交互操作,就是进行数据的read读取和write写入。

通道的读取,就是将数据从通道读取到缓冲区中;通道的写入,就是将数据从缓冲区中写入到通道中。

4.Selector(选择器)

对多个文件描述符的监视

指的是一个进程/线程可以同时监视多个文件描述符(一个网络连接,操作系统底层使用一个文件描述符来表示),一旦其中的一个或者多个文件描述符可读或者可写,系统内核就通知该进程/线程。