一、主要的IO模型

1、阻塞与非阻塞

        阻塞IO指的是需要内核IO操作彻底完成后才返回到用户空间执行用户程序的操作指令。“阻塞”指的是用户程序(发起IO请求的进程或者线程)的执行状态。可以说传统的IO模型都是阻塞IO模型,并且在Java中默认创建的socket都属于阻塞IO模型。

2、同步与异步

        简单来说,可以将同步与异步看成发起IO请求的两种方式。同步IO是指用户空间(进程或者线程)是主动发起IO请求的一方,系统内核是被动接收方。异步IO则反过来,系统内核是主动发起IO请求的一方,用户空间是被动接收方。

3、同步/异步、阻塞/非阻塞之间的关系

        同步、异步、阻塞与非阻塞可以组合成以下4种排列:

        1)同步阻塞

        2)同步非阻塞

        3)异步阻塞

        4)异步非阻塞

        在使用普通的InputStream、OutputStream类时,就是属于同步阻塞,因为执行当前读写任务一直是当前线程,并且读不到或写不出去就一直是阻塞的状态。阻塞的意思就是方法不返回,直到读到数据或写出数据为止。

        NIO技术属于同步非阻塞。当执行“serverSocketChannel.configureBlocking(false)”代码后,也是一直由当前的线程在执行读写操作,但是读不到数据或数据写不出去时读写方法就返回了,继续执行读或写后面的代码。

        而异步当然就是指多个线程间的通信。例如,A线程发起一个读操作,这个读操作要B线程进行实现,A线程和B线程就是异步执行了。A线程还要继续做其他的事情,这时B线程开始工作,如果读不到数据,B线程就呈阻塞状态了,如果读到数据,就通知A线程,并且将拿到的数据交给A线程,这种情况是异步阻塞。

        最后一种是异步非阻塞,是指A线程发起一个读操作,这个读操作要B线程进行实现,因为A线程还要继续做其他的事情,这时B线程开始工作,如果读不到数据,B线程就继续执行后面的代码,直到读到数据时,B线程就通知A线程,并且将拿到的数据交给A线程。

        从大的概念上来讲,同步和异步关注的是消息通信机制,阻塞和非阻塞关注的是程序在等待调用结果时的状态。文件通道永远都是阻塞的,不能设置成非阻塞模式。

        首先一个I/O操作其实分成了两个步骤:

        1)发起I/O请求;

        2)实际的I/O操作。

        同步I/O和异步I/O的区别就在于第二个步骤是否阻塞。如果实际的I/O读写阻塞请求进程,那么就是同步I/O。因此,阻塞I/O、非阻塞I/O、I/O复用、信号驱动I/O都是同步I/O。如果不阻塞,而是操作系统帮用户做完IO操作再将结果返回给用户,那么就是异步I/O。

        阻塞I/O和非阻塞I/O的区别在于第一步,即发起I/O请求是否会被阻塞。如果阻塞直到完成,那么就是传统的阻塞I/O;如果不阻塞,那么就是非阻塞I/O。

4、IO 模型

        服务端高并发IO编程往往要求的性能都非常高,一般情况下需要选用高性能的IO模型。

(1)阻塞 IO 模型

        同步阻塞IO(Blocking IO)指的是用户空间(或者线程)主动发起,需要等待内核IO操作彻底完成后才返回到用户空间的IO操作。在IO操作过程中,发起IO请求的用户进程(或者线程)处于阻塞状态。

(2)非阻塞 IO 模型

        非阻塞IO(Non-Blocking IO,NIO)指的是用户空间的程序不需要等待内核IO操作彻底完成,可以立即返回用户空间去执行后续的指令,即发起IO请求的用户进程(或者线程)处于非阻塞状态,与此同时,内核会立即返回给用户一个IO状态值。

        阻塞和非阻塞的区别是什么呢?阻塞是指用户进程(或者线程)一直在等待,而不能做别的事情;非阻塞是指用户进程(或者线程)获得内核返回的状态值就返回自己的空间,可以去做别的事情。在Java中,非阻塞IO的socket被设置为NONBLOCK模式。

同步非阻塞IO也可以简称为NIO,但是它不是Java编程中的NIO。Java编程中的NIO(New IO)类库组件所归属的不是基础IO模型中的NIO模型,而是IO多路复用模型。

        同步非阻塞IO指的是用户进程主动发起,不需要等待内核IO操作彻底完成就能立即返回用户空间的IO操作。在IO操作过程中,发起IO请求的用户进程(或者线程)处于非阻塞状态。

(3)多路复用 IO 模型

        为了提高性能,操作系统引入了一种新的系统调用,专门用于查询IO文件描述符(含socket连接)的就绪状态。在Linux系统中,新的系统调用为select/epoll系统调用。通过该系统调用,一个用户进程(或者线程)可以监视多个文件描述符,一旦某个描述符就绪(一般是内核缓冲区可读/可写),内核就能够将文件描述符的就绪状态返回给用户进程(或者线程),用户空间可以根据文件描述符的就绪状态进行相应的IO系统调用。

        IO多路复用(IO Multiplexing)属于一种经典的Reactor模式实现,有时也称为异步阻塞IO,Java中的Selector属于这种模型。

(4)异步 IO 模型

        异步IO(Asynchronous IO,AIO)指的是用户空间的线程变成被动接收者,而内核空间成为主动调用者。在异步IO模型中,当用户线程收到通知时,数据已经被内核读取完毕并放在了用户缓冲区内,内核在IO完成后通知用户线程直接使用即可。

        异步IO类似于Java中典型的回调模式,用户进程(或者线程)向内核空间注册了各种IO事件的回调函数,由内核去主动调用。

(5)信号驱动 IO 模型

        首先应用进程通过sigaction系统调用安装一个信号处理函数(在内核位置),该系统调用立即返回,进程继续工作(未被阻塞)。当数据准备就绪时,kernel就会为该进程产生一个SIGIO信号,随后用户进程就可以使用recvfrom调用去读取数据到内存,并返回成功指示。

二、C++/Java中的IO多路复用分析

1、QT/C++

         c++:下面这个博客,分析了QT/C++实现的TcpServer过程。

QT源码分析:QTcpServer - 平凡的编程者 

        下面的这个c++版本,感觉是作者参考Java实现的一套差不多一样的。GitHub - mogware/nio: C++ class library for non-blocking networking I/O (like java.nio)C++ class library for non-blocking networking I/O (like java.nio) - GitHub - mogware/nio: C++ class library for non-blocking networking I/O (like java.nio)

如何创建一个IO模型java java中的io模型_java

https://github.com/mogware/nio

 2、Java

        Java相关参考就不计其数了,卷的已经恨不得把源码一行一行翻译成汇编写出来。

        不过Selector选择器是NIO技术中的核心组件,可以将通道注册进选择器中,其主要作用就是使用1个线程来对多个通道中的已就绪通道进行选择,然后就可以对选择的通道进行数据处理,属于一对多的关系,也就是使用1个线程来操作多个通道,这种机制在NIO技术中称为“I/O多路复用”。它的优势是可以节省CPU资源,因为只有1个线程,CPU不需要在不同的线程间进行上下文切换。线程的上下文切换是一个非常耗时的动作,减少切换对设计高性能服务器具有很重要的意义。

Java NIO Channel

https://www.tutorialspoint.com/java_nio/java_nio_quick_guide.htm

Java AIO - 异步IO详解 - 知乎