最近开始刷书,整理知识点连载...
常见的IO模型有4中,对此总结如下
1.同步阻塞IO(Blocking IO)
阻塞IO,指需要内核IO操作彻底完成后,才返回到用户空间执行用户的操作。阻塞的是用户空间执行的状态。
传统IO模型都是同步阻塞IO,在java里默认的socket都是阻塞的。
同步IO,是一种用户空间与内核空间的IO发起方式。同步IO是指用户空间的线程是主动发起IO请求的一方,内核空间是被动方。
2.同步非阻塞IO(Non-blocking IO)
非阻塞IO,指的是用户空间的程序不需要等待内核IO操作彻底完成,可以立即返回用户空间执行用户的操作,即处于非阻塞的状态,与此同时内核会立即返回给用户一个状态值。
3.IO多路复用(IO Multiplexing)
Reactor反应器模式,也称异步阻塞IO,Java中的Selector选择器和Linux的epoll都是这种模型。
4.异步IO(Asynchronous IO)
这个不了解,目前没啥用
接下来看看
同步阻塞IO(Blocking IO):
整体上来说就是等等等...从你用Java启动IO调用read开始,系统就进去阻塞,然后系统内核收到read调用,开始主备需要的数据,此时数据可能还没到达内核缓冲区,这是内核就要等...内核需要等完整的数据到达,才可以将内核缓冲区的数据复制到用户缓冲区,然后内核返回结果。等到内核返回后,用户线程才会解除阻塞状态,重新跑起来。
总结是在内核进行IO的两个阶段,用户线程都被阻塞住了,用户量小的情况下可以用,高并发切记勿用。
同步非阻塞NIO(None Blocking IO):
socket连接默认是阻塞的,在linux里,我们可以设置socket编程非阻塞模式(Non-Blocking)。使用非阻塞模式的IO独写,叫做同步非阻塞IO(None Bolcking IO),简称NIO,不是java里的New IO。
在NIO模型中,应用程序一开始IO系统调用,会出现以下两种情况:
(1)在内核缓冲区没有数据的情况下,系统调用会立即返回,返回一个调用失败的信息。(所以为了拿到数据就要一直发起IO调用)
(2)在内核缓冲区中有数据的情况下,是阻塞的,直到数据从内核缓冲区复制到用户进程缓冲。
赋值完成后,系统调用返回成功,应用进程开始处理用户空间的缓存数据。
总结:一直轮询有数据就读,没数据就轮询,并且可以时时返回数据,实时性很好,但是问题就是不断轮询内核听上去就呵呵了,这个东西了解就好了,应该不会直接用到吧。
接下来重点来了:
IO多路复用模型(IO Multiplexing):
如何避免同步非阻塞IO模型中轮询等待的时间问题呢?那就用IO多路复用模型吧!
在IO多路复用模型里,引入一种新的系统调用,一个进程可以监视多个文件描述符,对应的系统调用为select/epoll系统调用。通过该系统调用,一个进程可以监视多个文件描述符,一旦某个描述符就绪(一般指内核缓冲区可读/可写),内核能够将就绪的状态发回给应用程序。随后,应用程序根据就绪的状态,进行相应的IO系统调用。
目前支持IO多路复用的系统调用,有select、epoll等等。select系统调用,可以不断地轮询操作系统上都有支持,具有良好的跨平台特性。(select、poll、epoll后续单独出文章介绍吧,仿佛也是面试重点)。
总结:描述一下多路复用的调用过程。选择器注册。在这种模式下,首先,将需要read操作的目标socket网络连接,提前注册到select/epoll选择器中,Java中对应的选择器类是Selector类。然后,才可以开启整个IO多路复用模型的轮询流程。然后就绪状态的轮询,再然后用户线程获得就绪状态列表后,就可以根据socket连接发起调用,此时用户线程阻塞。内核开始赋值数据,将数据从内核缓冲区复制到用户缓冲区。复制完成后,内核返回结果,用户线程才会解除阻塞状态,用户线程读取到数据继续执行。
IO多路复用模型的缺点:本质上,select/epoll系统调用是阻塞式的,属于同步IO。都需要在读写时间就绪后,由系统调用本身负责进行读写,也就是说这个读写过程是阻塞的。