写在前面
本文一起看下常见的io模型。
1:基础知识
同步异步,阻塞阻塞,区别如下:
同步异步:描述的通信模式,即结果如果是主动的获取则是同步,处理结果是被动的接收则是异步
阻塞非阻塞:描述的是线程的处理模式,即关注线程的状态
究竟啥意思?意会吧!!!
2:io模型
为了提升io的效率,不同的io模型被提了出来,如下图一共5中:
2.1:阻塞IO模型
对应Java的BIO,一种阻塞同步的IO模型,应用程序执行read操作后,一直阻塞等待,直到内核将数据从内核内存拷贝到用户内存,如下图:
2.2:非阻塞IO模型
不同于阻塞io的地方在于,线程不会阻塞,而是直接返回先忙其他事情,之后再定期的查询数据准备情况,但从通信模式看依然是同步的,因为需要主动获取结果,所以这是一种同步非阻塞的io模型,对应Java的就是NIO,即non blocking io,如下图:
2.3:非阻塞IO模型
io多路复用,这种io模型类似于非阻塞IO的地方在于都有一个定期轮询数据状态的操作,但是不同之处在这个轮询的动作由内核执行select/poll来完成,然后业务线程只需要一个来阻塞等待就行了,并且内核轮询的不是一个socket,而是一组socket,所以效率会有比较大的提升,如下图:
常规的select和poll实现,包含如下的不足:
1:需要将fd从内核拷贝到用户空间
2:所有的fd都需要遍历,效率低
3:支持的fd个数少,为1024个
在Linux 3正式发布了epoll,具备如下优点:
1:开辟了内核和用户公用的内存空间,解决了fd从内核拷贝到用户空间的问题
2:通过回调解决需要轮询fd的问题
3:fd的个数限制支持达到了10万级,则可支持10万的连接
参考下图:
注意到上图就没有数据从内核拷贝到用户空间的过程了,因为有了内核和用户控件公用的内存空间。
2.4:信号驱动io模型
这种方式不需要阻塞等待数据,也不需要轮询数据,只需要给内核发送一个信号,告知其需要什么数据,之后内核在数据准备完毕之后,同样会给应用程序一个信号,让其来读取数据,这个过程应用线程不阻塞,但读取数据的过程依然是阻塞,因此这是一种同步非阻塞的io模型,如下图:
2.5:异步io
这是唯一的一种真正支持异步的io模型,用户进程调用内核后直接返回,内核在数据准备完毕后,会将数据从内核拷贝到用户进程缓冲区,然后再给用户进程发送信号(或者回调,不管哪种方式数据对于用应用进程来说已经是直接可以使用的状态了),告知应用进程数据准备完毕(这点和信号驱动不同,信号驱动是在数据到内核的时候就发出信号,所以信号驱动不是异步的,因为还需要用户进程阻塞的拷贝数据到用户进程缓冲区),如下图:
目前异步io这种模型应用的并不多,因为广泛应用在服务器的linux系统并不支持,当前支持这种io模型的是Windows的iocp模型。