IO模型(Netty,Redis,Zookeeper高并发实战整理)

IO的基本概念

read系统调用,并不是直接从物理设备把数据读取到内存中;write系统调用,也不是直接把数据写入到物理设备。上层应用无论是调用操作系统的read,还是调用操作系统的write,都会涉及缓冲区。具体来说,调用操作系统的read,是把数据从内核缓冲区复制到进程缓冲区;而write系统调用,是把数据从进程缓冲区复制到内核缓冲区。

缓冲区意义

外部设备的直接读写,设计操作系统的中断,发生系统中断时,需要保存之前的进程数据和状态等信息,而结束中断之后,还需要恢复之前的进程数据和状态等信息,为了减少这种底层系统的时间损耗,性能损耗,于是在上层应用和下层硬件之间有出现了内存缓冲区。

有了内存缓冲区,上层应用使用read系统调用是,仅仅把数据从内核缓冲区去复制到上层应用的缓冲区(进程缓冲区);上层应用使用write系统调用是,仅仅把数据从进程缓冲区复制到内核缓冲区。

底层操作会对内核缓冲去进行监控,等待缓冲去达到一定数量的时候,在进行IO设备的终端处理,集中执行物理设备的实际IO操作,这种机制提升了系统的性能。

Linux系统中,操作系统内核只有一个内核缓冲区,而每个用户程序(进程),有自己独立的缓冲区,叫做进程缓冲区。所以,用户程序的io读写程序,在大多数情况下,并为没有进行实际的IO操作,二十在进程缓冲区和内核缓冲区之间直接进行数据的交换。

IO模型

同步阻塞IO(Blocking IO)

阻塞与非阻塞

阻塞IO,指的是需要内核io操作彻底完成后,才返回到用户空间执行用户的操作。阻塞指的是用户空间程序的执行状态。

同步与异步

同步IO,是一种用户空间与内核空间IO发起方式。同步io是指用户空间的线程是主动发起io请求的一方,内核空间是被动接受方。异步io,是这系统内核主动发起IO请求的一方,用户空间的线程是被动接受方。

BIO调用流程

在java应用程序进程中,默认情况下,所有的socketk连接的IO操作都是同步阻塞io,在阻塞式io模型中,java应用程序从io系统调用开始,直到系统调用返回,在这段时间内,java进程是阻塞的。放回成功后,应用进程开始处理用户空间的缓存区数据。

在java中发起一个socket的reed读操作的系统调用,流程大致如下:

1.从java启动io读的read系统调用开始,用户线程就进入阻塞状态
2.当系统内核收到read系统调用,就开始准备数据。一开始,数据可能还没有到达内核缓冲区,这个时候内核就要等待。
3.内核一直等到完整的数据到达,就会讲数据从内核缓冲区复制到用户缓冲区(用户空间的内存),让后内核返回结果(例如返回复制到用户缓冲区中的字节数)。
4.直到内核返回后,用户线程才会解除阻塞的状态,重新运行起来。

特点

阻塞io在执行的两个阶段,用户线程都被阻塞了。

优点

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

缺点

一般情况下,会为每个连接配备一个独立的线程;反过来说,就是一个线程维护一个连接的io操作。在并发量小的情况下,可用。不适合高并发的情况下。

同步非阻塞io(Non-blocking IO)

非阻塞Io,指定的是用户空间的程序不要等待内核IO操作彻底完成,可以立即返回用户空间执行用户的操作,即处于非阻塞状态,于此同时内核会立即放回给用户一个状态值。

阻塞是指用户空间(调用线程)一直在等待,而不能干别的事情;非阻塞是指用户空间(调用线程)拿到内核返回的状态值就返回自己的空间,IO操作就可以干就干,不可以干,就去干别的事情。

非阻塞io要求socket被设置成NOBBLOCK

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

1.在内核缓冲区中没有数据的情况下,系统调用会立即放回,返回一个调用失败的信息。
2.在内核缓冲区中有数据的情况下,是阻塞的,直到数据从内核缓冲区复制到用户进程缓冲区。复制完成后,系统调用返回成功,应用程序开始处理用户空间的缓冲数据。

NIO调用流程

在java中发起一个非阻塞socket的reed读操作的系统调用,流程大致如下:

1.在内核数据没有准备好的阶段,用户线程发起IO请求时,立即返回。所以为了读取到数据,用户线程需要不断地发起IO系统调用。查询是否内核空间有要访问的数据
2.内核数据到达后,用户线程发起系统调用,用户线程阻塞。内核开始复制数据,内核会将数据从内核缓冲区复制到用户缓冲区(用户空间的内存),然后内核返回结果(例如返回复制到的用户缓冲区的字节数)。
3.用户线程读到数据后,才会接触阻塞状态,重新运行起来。用户线程需要经过多次的调用io才能保证最终正确读到数据,进而继续执行。

特点

应用程序的线程需要不断地进行IO系统调用,轮询数据是否已经准备好;如果没有准备好,就继续轮询,知道io系统调用为止。

优点

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

缺点

不断地轮询内核,这将占用大量的cpu时间,效率低下

特别声明:

同步非阻塞io,可以简称NIO,但是不是Java中的NIO,虽然他们的缩写一样。但Java的NIO对应的是IO多路复用器模型。

IO多路复用器(IO Muliplexing)

即经典的Reactor反应器设计模式,又是又称异步阻塞IO,java中的Selector选择器和linux中的epoll都是这种模型。

在IO多路复用器模型中,引用了一种新的系统调用,查询IO的就绪状态。在Linux系统中,对应的系统调用为select/epoll系统调用,一个进程可以监视多个文件描述符,一旦某个文件描述符就绪(一般是内核缓冲区可读/可写),内核能够将就绪的状态返回给应用程序。随后,应用程序根据就绪状态,进行相应的IO系统调用。

在IO多路复用器模型中通过select/epoll系统调用,单个应用程序的线程,可以不断轮询成百上千的socket连接,当某个或者某些socket网络连接有IO就绪的状态,就返回对应的可以执行的读写操作。、

IO多路复用器调用流程

发起一个多路复用IO的read读操作的系统调用,流程如下:

1,选择器注册,这种模式中,首先,讲需要read操作的目标socket网络连接,提前注册到select/epoll选择器中,Java的选择器类是Selector类,然后才可以开启整个IO多路复用模型的轮询流程。
2.就绪状态的轮询。通过选择器的查询方法,查询注册过的所有socket连接的就绪状态。通过查询的系统调用,内核会返回一个就绪的socket列表。当任何一个注册过的socket中的数据准备好了,内核缓冲区有数据(就绪)了,内核就讲socket加入就绪的列表中。当用户进程调用了select查询方法,那么整个线程会被阻塞掉。
3.用户线程获得到了就绪状态的列表后,根据其中的socket连接,发起read系统调用,用户线程阻塞。内核开始复制数据,将数据从内核缓冲区复制到用户缓冲区。
4.复制完成后,内核返回结果,用户线程才会解除阻塞的状态,用户线程读取到了数据,继续执行。

特点

IO多路复用器模型的IO涉及两种系统调用,一种是IO操作,另一种是select/epoll(查询数据的状态)。IO多路复用模型建立在操作系统的基础设施之上,即操作系统的内核必须能够提供多路复用的系统调用select/epoll.

优点

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

缺点

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

异步IO(Asychronous IO)

异步io,指的是用户空间与内核空间的调用方式反过来。用户空间的线程变成被动接受者,而内核空间变成了主动调用这。这有点类似与java中比较典型的回调模式,用户空间的线程想内核空间注册了各种IO事件的回调函数,有内核去主动调用。

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

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

AIO调用流程

发起一个异步IO的read读操作的系统调用,流程如下:

1.当用户线程发起了read系统调用,立刻就可以区开始区做其他的事了,用户线程不阻塞。
2.内核就开始IO的第一个阶段:准别数据。等数据准备好了,内核就会将数据从内核缓冲区复制到用户缓冲区。
3.内核会给用户线程发送一个信号,或者回调用户线程注册的回调接口,告诉用户线程read操作完成了。
4.用户线程读取用户缓冲区的数据,完成后续的业务操作。

特点

在内核准备数据和复制数据的两个阶段,用户线程都不是阻塞的。用户线程需要接收内核的IO操作完成的事件,或者用户线程需要注册一个IO操作完成的回调函数。正应为如此,异步IO也被称为信号驱动IO。

缺点

应用程序仅需要进行事件的注册与接受,其余工作都留给了操作系统,需要底层内核提供支持。异步IO事真正的异步IO输入输出,吞吐量高于IO多路复用器的吞吐量。Windows系统下通过IOCP实现了真正的异步IO。而在Linux系统下,异步IO模型不完善,底层实现还是epoll,与IO多路复用相同。

参考书籍:
《Netty,Redis,Zookeeper高并发实战》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章