同步、异步、阻塞、非阻塞IO

简介

最近在整理过去一段时间内Netty性能调优的工作,涉及到了Netty的NIO/Epoll等异步非阻塞通信,索性复习一下常见的几种IO模型。看到CSDN上这篇文章写得很清晰易懂,IO - 同步,异步,阻塞,非阻塞 (亡羊补牢篇),在此基础上整理了一下,方便记忆及以后查看。

IO模型

IO发生时会经历两个阶段:

  • 等待数据

  • 将数据从内核复制到用户空间

不同的IO模型就是根据在这两个阶段所做的处理不同进行区分的。

Blocking IO

blocking IO.jpg

阻塞IO模型(BIO):应用进程调用recvfrom,kernel开始了IO的第一阶段:等待数据,等到数据准备好了,kernel开始第二阶段:将数据从内核复制到用户空间。在IO的这两个阶段执行期间,应用进程一直处于block状态,直到kernel返回结果,应用进程阻塞才解除。

举例:你在美团上点了一份外卖,下单后外卖小哥开始取餐(阶段一),取到餐后再进行配送(阶段二),期间你一直站在门口等(block状态),直到收到外卖才继续自己的事情。

Nonblocking IO

non-blocking IO.jpg

非阻塞IO模型(NIO):应用进程调用recvfrom,kernel开始了IO的第一阶段:等待数据,如果kernel中的数据还没有准备好,就返回应用进程EWOULDBLOCK,应用进程收到返回后会一直调用recvfrom询问kernel,直到数据准备好,在此期间应用进程处于非block状态;然后kernel将数据从内核复制到用户空间,并block应用进程,直到成功返回后阻塞状态解除。所以非阻塞IO和阻塞IO不是完全对立的,在IO第二阶段二者均处于block状态。

举例:你在美团上点了一份外卖,下单后外卖小哥开始取餐(阶段一),在取餐期间你坐在屋里一遍看剧一边不断打电话给外卖小哥询问取到了没,直到小哥说他拿到了;这时小哥取到餐后进行配送(阶段二),你也起身站在门口等(block状态),直到收到外卖。

IO multiplexing

IO multiplexing.jpg

IO复用模型:应用进程调用select,通过select可以不断的轮询所负责的所有socket,当某个socket的数据准备好了,select就会返回信息,然后用户态进程调用recvfrom,将数据从内核复制到用户空间。对于每一个socket,一般都设置成为non-blocking,但是整个应用进程受阻于select调用;同样,在IO的第二阶段,应用进程也是出于block状态。

如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll的优势并不是对于单个连接能处理得更快,而是在于单个process就可以同时处理多个网络连接的IO。

Linux关于IO复用的使用,有三种不同的API,select、poll和epoll,关于这三种API的实现分析,可以参考下这篇文章:select、poll、epoll之间的区别总结[整理]

举例:这次你在美团上一次点了好几份外卖,美团的消息系统(select等API调用)推给你说等有外卖取到了就发消息给你,让你留意消息(block状态),然后几个外卖小哥就到不同的店去取餐(阶段一)。等取到餐了,如果你用的是select或poll版本的美团,消息推给你说你有订单已经取到了,但是不告诉你是哪家;如果你用的是epoll版本的美团,消息推给你说你有订单已经取到了,而且还告诉你是碳烤羊排。收到消息之后你就到门口去等(block状态),外卖小哥进行配送(阶段二),直到收到外卖才继续自己的事情。

Asynchronous IO

Asynchronous IO.jpg

异步IO模型(AIO):应用进程调用aio_read,kernel收到请求后会立刻返回,等数据准备好,并且复制到用户空间后执行事先指定好的函数,整个过程应用进程都处于非block状态。

举例:你发现自己出去等太大笨蛋了,在美团上点了一份外卖,看到下单成功后就去看剧了。外卖小哥开始取餐(阶段一),取到餐后再进行配送(阶段二),最后送到你门口,整个过程你都在干自己的事情,没有一直等着(非block状态)。

总结

IO模型对比

IO Models.jpg

因为信号驱动式IO(signal-driven I/O)使用较少,正文中没提及。

阻塞IO和非阻塞IO:调用blocking IO会一直block住对应的进程直到操作完成,而non-blocking IO在kernel还准备数据的情况下会立刻返回。

同步IO和异步IO:synchronous IO做”IO operation”的时候会将process阻塞,blocking IO,non-blocking IO,IO multiplexing因为都阻塞于recvfrom调用,都属于synchronous IO。

非阻塞IO和异步IO:在数据准备阶段,non-blocking IO中进程不会被block,但是它仍然要求进程去主动的check,而asynchronous IO中应用进程进程将整个IO操作交给kernel来完成。