如何理解常见的IO模型:阻塞、非阻塞、多路复用、异步

如何理解常见的IO模型:阻塞、非阻塞、多路复用、异步

本篇内容主要讲解“如何理解常见的IO模型:阻塞、非阻塞、多路复用、异步”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何理解常见的IO模型:阻塞、非阻塞、多路复用、异步”吧!

在Unix网络编程中,史蒂文斯给出了5种IO编程模型,其中最重点、也最常用的是多路复用模型(Multiplexing)。 这5种模型分别为:

  • 阻塞式IO

  • 非阻塞式IO

  • IO多路复用(multiplexing io),基于select/poll/epoll

  • 信号驱动式IO SIGIO

  • 异步IO(posix aio_abi和libaio)

想要说明清楚这几个模型,一个很好的方式是把网络IO分为两个阶段来理解。第一阶段,从客户端向服务端发送请求开始,到数据从网络传输到达,完全准备好为止。第二阶段,数据从内核空间复制到程序缓存(即用户空间),这个阶段才是应用真正执行recvfrom系统调用的阶段。在第一阶段,程序要么阻塞在recvfrom调用上,要么阻塞在select之类的方法上,或者干其他的事情去了(轮询、异步等)。

如何理解常见的IO模型:阻塞、非阻塞、多路复用、异步

有了这个基本认知,我们再来逐个审视这几种模型。

阻塞式I/O模型

阻塞式IO(blocking I/O)是最基本的IO模型,也是日常使用中默认的模式。

如何理解常见的IO模型:阻塞、非阻塞、多路复用、异步

如上图,应用发出请求,试图执行recvfrom系统调用,以获取数据,由于数据还没准备好(也许服务端才开始处理请求),当前请求线程被阻塞,只能傻傻的等待,直到数据可读或者抛出异常。如果是单线程应用,主线程挂起,CPU空置。如果是多线程,当前线程挂起,CPU切换时间片去执行其他线程。

非阻塞式IO模型

一般的Socket对象都会有一个setblocking(False)或者ConfigureBlocing(false)之类的方法,将当前IO线程设置成非阻塞模式。

如何理解常见的IO模型:阻塞、非阻塞、多路复用、异步

如图,当socket设置为nonblocking时,在一阶段(数据准备阶段,还记得前面说的二阶段IO么,这里派上了用场)线程会不断的发起recvfrom轮询,如果还没有准备好数据,会得到一个EWouldBLOCK错误信号,直到数据准备好之后,开始真正执行recvfrom拉取数据。轮询会极大的消耗CPU时间,所以这种模型极少用到。

IO多路复用

I/O复用,即I/O multiplexing,这是一种基于select函数的编程模型,也是最常用的一种。有些人喜欢把它称作异步阻塞模型,我觉得这种叫法很容易让人产生误解,实际上这个模型和所谓的异步I/O没有半毛钱关系。

如何理解常见的IO模型:阻塞、非阻塞、多路复用、异步

如上图,在第一阶段,我们可以通过select函数注册多路IO对象。每一个注册的IO对象都会阻塞在select函数上,直到IO对象的状态发生改变。此时数据已经准备好读/写。调度器遍历这些IO对象,返回准备好读/写的IO对象。紧接着进入第二阶段,开始对准备好的IO对象调用recvfrom函数。

对于多路复用模型,Java NIO基于select库实现了调度器Selector,python的selectors模块分别提供了基于selectpoll以及epoll库的封装对象。

通过以上过程,我们还可以看到,其实IO多路复用阻塞IO很相似,基本两个阶段都在阻塞状态。只不过前者第一阶段阻塞在select函数上,第二阶段阻塞在recvfrom调用上;而后者全程阻塞在recvfrom上。而且IO多路复用由于需要注册、遍历IO对象,其实涉及到更多的步骤开销。但是多路复用的优势正在于可以同时对接多个IO对象,结合多线程技术,可以带来很大的灵活性。

异步IO模型

前面提到的三种模型,本质上在真正的IO阶段(第二阶段),都会阻塞。而在异步IO模型中,应用发出数据请求后,不再等待,直接返回。期间线程也不会阻塞。之后由内核处理两个阶段IO,然后给应用发送信号,程序直接获取数据。

如何理解常见的IO模型:阻塞、非阻塞、多路复用、异步

基于Unix系统有一个POSIX异步IO库——aio_abi,以及一个第三方库aiolib,后者可能更加知名。异步IO模型非常复杂,一般很少见到。另外我也有一些疑惑,python中的asyncio包提出的协程概念,和这里的异步IO是否为同一件事物。史蒂文斯在这里描述的异步明显需要基于系统内核的相关实现,和asyncio这种单纯的编程概念,大概说的不是一个层级的东西吧。

隐喻

最后还是说一个比喻吧。如果把IO模型比作网购,在你提交订单的一刻到快递员将货物带到你家小区门口期间,可看作一阶段IO。之后小哥给你打电话:“你的快递,快开门”,这相当于数据准备好了,并发送了通知。之后,你跑到门口将快递一件一件搬到家里,这是第二阶段。

阻塞模型相当于你提了订单,就不吃不喝,只等着快递,直到小哥给你打电话,然后吭哧吭哧把快递搬到家,你才开始吃喝、睡觉等。非阻塞模型下,你提交订单后就开始*行动了。但是仍然会每隔几分钟跑到小区门口看看快递到了没(轮询),一直把自己搞得精疲力尽。直到小哥给你打电话,你把快递搬回家才算消停。多路复用模型下,你同时提交了多个订单,但不再傻傻的朝门口来回跑了,而是紧紧盯着菜鸟裹裹信息(阻塞在select),直到其上显示至少有一个快递到了,你会嗖的跑过去,当然,快递仍然自己搬(recvfrom阻塞)。异步模型下,你雇了一个万能管家吉福斯,你只要下完订单,就不用操心了,他会打点好一切,最后他会把快递按照你们约定的地点、方式,直接送到你的手上。

到此,相信大家对“如何理解常见的IO模型:阻塞、非阻塞、多路复用、异步”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!