专栏名称: JavaGuide
专注Java知识分享!开源 Java 学习指南——JavaGuide(17k+ Star)的作者。公众号多篇文章被各大技术社区转载。
目录
相关文章推荐
中国日报网  ·  被传豪赌输10亿?黄晓明4字回应 ·  3 天前  
西湖之声  ·  女歌手全国巡回演唱会后被抓! ·  3 天前  
西湖之声  ·  女歌手全国巡回演唱会后被抓! ·  3 天前  
51好读  ›  专栏  ›  JavaGuide

漫话:如何给女朋友解释什么是Linux的五种IO模型?

JavaGuide  · 公众号  ·  · 2019-02-23 18:30

正文

漫小画

擅长漫话

程小员

擅长编程

周日午后,刚刚放下手里的电话,正在给刚刚的面试者写评价。刚刚写到『对Linux的基本IO模型理解不深』这句的时候,女朋友突然出现。


哈,这个面试者咋不知道IO模型呢,我都知道呢。




你怎么知道呢,你给我说说。


上次你给我讲过呀。



在Java中,主要有三种IO模型,分别是阻塞IO(BIO)、非阻塞IO(NIO)和 异步IO(AIO)。



额、你说的这个是Java中提供的IO有关的API啊。并不是操作系统层面的IO模型呢。


这有啥区别吗?他们有啥关系吗?



Java中提供的IO有关的API,在文件处理的时候,其实依赖操作系统层面的IO操作实现的。 比如在Linux 2.6以后,Java中NIO和AIO都是通过epoll来实现的,而在Windows上,AIO是通过IOCP来实现的。

可以把Java中的BIO、NIO和AIO理解为是Java语言对操作系统的各种IO模型的封装。程序员在使用这些API的时候,不需要关心操作系统层面的知识,也不需要根据不同操作系统编写不同的代码。只需要使用Java的API就可以了。

哦。那这个我不懂,你给我讲讲吧。




好吧,那就给你简单介绍一下吧。


嗯嗯,好的,讲的好了的话,晚上给你做红烧鱼吃。




嗯嗯,好的。


在Linux(UNIX)操作系统中,共有五种IO模型,分别是: 阻塞IO模型 非阻塞IO模型 IO复用模型 信号驱动IO模型 以及 异步IO模型

既然提到晚上吃鱼,那就通过钓鱼的例子来解释这五种IO模型吧。

到底什么是IO

我们常说的IO,指的是文件的输入和输出,但是在操作系统层面是如何定义IO的呢?到底什么样的过程可以叫做是一次IO呢?

拿一次磁盘文件读取为例,我们要读取的文件是存储在磁盘上的,我们的目的是把它读取到内存中。可以把这个步骤简化成把数据从硬件(硬盘)中读取到用户空间中。

其实真正的文件读取还涉及到缓存等细节,这里就不展开讲述了。关于用户空间、内核空间以及硬件等的关系如果读者不理解的话,可以通过钓鱼的例子理解。

钓鱼的时候,刚开始鱼是在鱼塘里面的,我们的钓鱼动作的最终结束标志是鱼从鱼塘中被我们钓上来,放入鱼篓中。

这里面的鱼塘就可以映射成磁盘,中间过渡的鱼钩可以映射成内核空间,最终放鱼的鱼篓可以映射成用户空间。一次完整的钓鱼(IO)操作,是鱼(文件)从鱼塘(硬盘)中转移(拷贝)到鱼篓(用户空间)的过程。

阻塞IO模型

我们钓鱼的时候,有一种方式比较惬意,比较轻松,那就是我们坐在鱼竿面前,这个过程中我们什么也不做,双手一直把着鱼竿,就静静的等着鱼儿咬钩。一旦手上感受到鱼的力道,就把鱼钓起来放入鱼篓中。然后再钓下一条鱼。

映射到Linux操作系统中,这就是一种最简单的IO模型,即阻塞IO。 阻塞 I/O 是最简单的 I/O 模型,一般表现为进程或线程等待某个条件,如果条件不满足,则一直等下去。条件满足,则进行下一步操作。

应用进程通过系统调用 recvfrom 接收数据,但由于内核还未准备好数据报,应用进程就会阻塞住,直到内核准备好数据报, recvfrom 完成数据报复制工作,应用进程才能结束阻塞状态。

这种钓鱼方式相对来说比较简单,对于钓鱼的人来说,不需要什么特制的鱼竿,拿一根够长的木棍就可以悠闲的开始钓鱼了(实现简单)。缺点就是比较耗费时间,比较适合那种对鱼的需求量小的情况(并发低,时效性要求低)。

这个钓鱼的人真傻,等鱼咬钩的时候可以做点别的事情呀。




嗯,你说的这种就是两外一种IO模型了。


非阻塞IO模型

我们钓鱼的时候,在等待鱼儿咬钩的过程中,我们可以做点别的事情,比如玩一把王者荣耀、看一集《延禧攻略》等等。但是,我们要时不时的去看一下鱼竿,一旦发现有鱼儿上钩了,就把鱼钓上来。

映射到Linux操作系统中,这就是非阻塞的IO模型。应用进程与内核交互,目的未达到之前,不再一味的等着,而是直接返回。然后通过轮询的方式,不停的去问内核数据准备有没有准备好。如果某一次轮询发现数据已经准备好了,那就把数据拷贝到用户空间中。

应用进程通过 recvfrom 调用不停的去和内核交互,直到内核准备好数据。如果没有准备好,内核会返回 error ,应用进程在得到 error 后,过一段时间再发送 recvfrom 请求。在两次发送请求的时间段,进程可以先做别的事情。

这种方式钓鱼,和阻塞IO比,所使用的工具没有什么变化,但是钓鱼的时候可以做些其他事情,增加时间的利用率。

这样确实好了一点了。鱼儿上钩之前我可以去淘宝挑两条裙子。




额,但是你还是要时不时的关注鱼竿的动向。


这还不好解决,买一个带提醒功能的鱼竿不就行了。




嗯,你说的又是另外一种IO模型了。


信号驱动IO模型

我们钓鱼的时候,为了避免自己一遍一遍的去查看鱼竿,我们可以给鱼竿安装一个报警器。当有鱼儿咬钩的时候立刻报警。然后我们再收到报警后,去把鱼钓起来。

映射到Linux操作系统中,这就是信号驱动IO。应用进程在读取文件时通知内核,如果某个 socket 的某个事件发生时,请向我发一个信号。在收到信号后,信号对应的处理函数会进行后续处理。

应用进程预先向内核注册一个信号处理函数,然后用户进程返回,并且不阻塞,当内核数据准备就绪时会发送一个信号给进程,用户进程便在信号处理函数中开始把数据拷贝的用户空间中。

这种方式钓鱼,和前几种相比,所使用的工具有了一些变化,需要有一些定制(实现复杂)。但是钓鱼的人就可以在鱼儿咬钩之前彻底做别的事儿去了。等着报警器响就行了。

嗯,这种方式最轻松啦。




是的。我问你啊,你还有什么好的方法可以最短时间内钓更多的鱼吗?


这还能难倒我么,同一时间摆放多个鱼竿同时钓呗。




好聪明,你说的又是另外一种IO模型了。


IO复用模型

我们钓鱼的时候,为了保证可以最短的时间钓到最多的鱼,我们同一时间摆放多个鱼竿,同时钓鱼。然后哪个鱼竿有鱼儿咬钩了,我们就把哪个鱼竿上面的鱼钓起来。







请到「今天看啥」查看全文