写在前面
本文一起看下都有哪些不同的IO模型,以及不同IO模型的特点和优缺点是什么。
1:阻塞非阻塞,异步和同步
阻塞和非阻塞,异步和同步是在含义上非常接近的概念,很容易混淆,我也不能保证完全理解到位,这里分享下自己的理解,有不正确的地方还请留言指正。
-
阻塞和非阻塞
站在应用程序的角度看,如果是发起IO调用后,应用程序线程会阻塞一直等到获取响应则就是阻塞IO,反之应用程序可以执行其他任务,而不阻塞等待则就是非阻塞IO。 -
异步和同步
站在系统的角度看,同步就是,在收到应用程序的IO请求后,系统不会立即返回响应,而是在处理完成后,才通过系统调用告知应用程序IO结果,异步就是系统收到应用程序的IO请求后,会直接告知应用程序IO请求已收到,之后异步开始处理,处理完成后,通过回调的方式告知应用程序处理结果。比如系统调用read就是同步读,在没有读到数据之前,系统不会响应应用程序。比如系统调用aio_read就是异步读,系统收到AIO的读请求后,直接返回,最终读取的结果会异步回调应用程序。
通过以上分析可以看到,阻塞和非阻塞是站在应用程序的角度看的,而同步和异步是站在系统的角度看到。
2:BIO
即Blocking IO,是一种同步阻塞的IO模型,每次read操作获取数据,会一直等待直到内核获取数据然后将数据拷贝到用户内存空间,这种IO模型的最大优点是不会浪费CPU资源,但是这种IO模型在高并发场景中并不适用,因为存在严重的性能问题。
3:NIO
注意不是Java NIO,Java的NIO是New IO,和这里分析的不是一回事,Java NIO使用的线程模型是IO多路复用。
这里的N是Non-blocking的意思,全称是Non-blocking IO,非阻塞IO, 但还是同步的,所以是同步非阻塞IO。不同于BIO,NIO模型在执行read操作时,当内核没有可以读取的数据时,会直接返回,这样用户线程就可以先执行其他任务了,之后会定时的执行read,查看内核是否有数据可读取,这个过程可参考下图:
NIO这种IO模型的最大问题是read轮询,这会导致占用大量的CPU时间片,降低CPU的利用率,在高并发场景下这种IO模型也并没有用武之地,而且因为是轮询,也会造成响应延迟增大的问题,从而会降低系统的吞吐量。
4:IO多路复用模型
是一种阻塞异步IO模型,是一种单个线程处理多个连接的IO模型,相比于BIO,NIO,只有read操作,IO多路复用模型增加了select操作,该操作也是一个阻塞操作,当存在某个socket fd,即某个连接可以进行读取数据的操作是,select函数返回,然后执行read操作获取数据,这个read获取数据操作的过程也是阻塞的,这个过程如下图:
当前Linux对于多路复用模型提供的具体支持有select,poll,epoll,其中后者都是前者的升级版本,目前使用最多的就是epoll。内核需要维护一组fd,当存在某fd可读数据时,select函数就返回可读连接,然后read读取操作,如果是具体的用户操作速度非常快的话,这种基于单线程的IO模型,性能上也是不会差的,比如redis就是基于此IO模型实现的数据操作。
5:AIO
A,Asynchronous,异步,即Asynchronous IO,异步IO,是唯一的一种异步非阻塞的IO模型,执行read操作后,线程就返回执行其他任务了,后续的数据准备,从内核控件拷贝到用户空间的操作,全部由内核完成,数据拷贝完成后调用应用程序注册的回调程序,将数据交给应用程序处理,参考下图:
写在后面
参考文章列表: