在了解IO模型前,我们先了解几个概念
阻塞与非阻塞
- 阻塞:
- 指调用结果返回之前,当前线程会被挂起,IO操作需要彻底完成后才返回到用户空间。
- eg:在烧水过程中,你不能去干别的事情,只能在这里等待。
- 非阻塞:
- 与阻塞相对应,指在不能立即得到结果之前,该函数不会阻塞当前线程,而会立即返回。
- eg:在同样的烧水过程中,你可以同时去干其他的事情。
简单来说,阻塞就是干不完不准回来,非阻塞就是你先干,我现看看有没有其他的事要做,完了告诉我一声
同步与异步
- 同步:
- 就是在客户端发出一个功能调用时,在没有得到结果之前,该调用不返回。
- eg:烧水时,需要自己去轮询查看(每隔一段时间去看看水烧开了没)
- 异步:
- 与同步相对应,当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。
- eg:烧水时,如果水开了,水壶会自己通知你水开了,你可以回来处理这些开水了。
介绍下面五种IO模型时,用小明买演唱会门票的例子来说明
一、阻塞式IO模型
- 介绍:
在内核将数据准备好之前, 系统调用会一直等待。
也就是说, 应用程序调用一个I/O函数,导致应用程序阻塞后,等待数据,如果数据没有准备好,一直等待;直到数据准备好,从内核拷贝到用户空间,I/O函数返回成功。 - 例子:
小明去售票点买票,但是售票员告诉小明说明天才可以买票,小明就在售票点等着,期间什么也不干,一直等到明天买到票才回家。 - 模型图:
- 过程:
- 调用recvfrom时,系统首先检查是否有准备好的数据,如果数据没有准备好,那么系统就处于等待状态;
- 当数据准备好后,将数据从系统缓冲区拷贝到用户空间,然后函数返回。
二、非阻塞式IO模型
- 介绍:
如果内核还未将数据准备好,系统调用会直接返回,并且返回EWOULDBLOCK错误码.
非阻塞就是当所请求的I/O操作无法完成时则返回一个错误;I/O函数会不断的测试数据是否准备好,直到数据准备好为止。 - 例子:
当小明从家到售票处买票时,被告知票还没有开始售卖,然后小明就走了,去干其他事情,过一会再次来询问票是否出来,若没出来,又去干别的事情,一直轮询。 - 模型图:
- 过程:
- 每次用户询问内核是否有数据报准备好,数据报准备好时,就进行拷贝数据报的操作。
- 当数据报没有准备好的时候,也不阻塞程序,内核直接返回未准备就绪的信号,等待用户程序的下一次轮询。
- 这是一个轮询的过程,对CPU来说是较大的浪费。
三、多路复用IO模型
- 介绍:
多路复用IO模型属于阻塞IO,但是它可以对多个文件描述符进行阻塞监听,所以它的效率比阻塞IO模型高效。
IO复用中,Java——selector;Linux——select,poll,epoll - 例子:
小明想买演唱会的门票,打电话给黄牛(selector)帮忙留意买个票,票出来后,小明需要花费时间去售票点取票。 - 模型图:
- 过程:
- 与阻塞式IO相比,多了一个select函数,对其参数中的文件描述符进行循环监听
- 当某个文件描述符就绪的时候,就对这个文件描述符进行处理。
- 直到有数据可读或可写时,才真正调用I/O操作函数
四、信号驱动IO模型
- 介绍:
由信号进行驱动 - 例子:
小明买演唱会门票时,先给举办方打电话,让出票后给他打电话告知他,他自己去买。票出来接到电话后,小明亲自去售票点买票。 - 模型图:
- 过程:
- 建立一个信号处理程序,进程可以继续运行并不阻塞。
- 当数据准备好时,进程会收到一个SIGIO信号
- 在信号处理函数中调用I/O操作函数处理数据报
五、异步IO模型
- 介绍:
由内核在数据拷贝完成时,通知应用程序 - 例子:
小明要看演唱会,给举办方打电话,给举办方打电话,可以买票的适合让送票快递员帮忙把票送到家里,小明就不用专门跑去买票。 - 模型图:
- 过程:
- 当应用程序调用aio_read时,内核一方面去取数据报内容,另外一方面将程序控制权还给应用进程,进程继续执行
- 应用进程继续处理其他事务(应用进程是非阻塞的状态)
- 当内核的数据报就绪的时候,由内核将数据报拷贝到应用进程中,返回给aio_read中定义好的函数处理程序
阻塞程度:阻塞IO>非阻塞IO>多路转接IO>信号驱动IO>异步IO,效率由低到高的