赞
踩
之前写过一篇《搞懂IO》,这篇算是对上一篇的补充吧。我们已经知道高级程序设计语言上的IO实际上依赖底层操作系统,可以说它是操作系统层面上的一个抽象吧。想想平时程序都部署在什么服务器上,可能是在linux,可能是在windows...不管怎样,别担心,既然是抽象,肯定支持刚才说的操作系统。基于更常见的是部署在linux系统,待会不得不说说linux下IO的五个模型。
你一定不止在一个地方看到类似"cpu等待IO就绪,线程挂起..."的描述,不知你有没有想过CPU到底在 等待个啥,等待个寂寞吗?为什么CPU要等待呢?CPU不能参与吗? 首先,cpu有两种状态,用户态和内核态,cpu会根据需要在两种状态之间切换。 用户态和内核态的cpu有什么区别呢? 想想古时候的将军,他手里握有兵权,但是他调用军队是需要有兵符的,没有兵符,他就没办法调兵遣将。 没了兵符的将军就是一个普通老百姓,只拥有有普通公民的权利。所以,一个人有没有持有兵符,拥有的权限不一样。 一个人有兵符,你就是将军(将军态,类似内核态),你就能调动军队资源;没有兵符,你就是一介布衣(布衣态,类似用户态),拥有普通权利而已。 cpu同理,内核态的cpu有较高的权限,它能调用一些高级别的资源,比如访问一些安全级别较高的地址空间。 其次,计算机中,存储是分结构的。 除此之外,主存(内存)这一层面内部同样进行了逻辑划分,分为用户空间和内核空间。 顾名思义,内核空间只有处于内核状态的cpu才能访问的。 一个军队里,将军不可能管理所有人,他会设立千夫长,百夫长来管理。 cpu同样将部分职责交给了另一个硬件,叫作DMA。 “读操作”时,DMA负责将数据从磁盘拷贝到内核空间,注意是拷贝不是移动。然后cpu再从内核空间拷贝到用户空间。 所以,cpu等待io就绪,在“读操作”时,等的就是DMA将数据从磁盘拷贝到内核空间。 为什么CPU要将它交给DMA搞,因为磁盘到内核空间的拷贝太慢了(想必你也知道不同级别的存储结构读写速度不一样), CPU的时间很宝贵的,它应该去做一些价值更大的东西(马云爸爸的一秒钟也值很多钱)。
- 在说到IO模型之前,先说说经常听到的阻塞,非阻塞,同步,异步这几个概念,它们是描述"某种操作"的,
- 并且这种操作一定是"耗时"的,否则没有意义。
- 经常容易混淆,在不同上下文中语意还有点不一样,这里就说说IO中的区别。
- 如果你去营业厅办理业务,"办理业务"这个事情其实可以细分为再小粒度的事情,"等待轮到你"和"实际处理业务"两件小事情。
- 线程调用read()方法,这个方法里,其实包括"等待IO就绪"和"实际读"操作,再具体一点
- 就是"等待数据拷贝到内核空间"和"cpu将数据从内核空间拷贝到用户空间"。
- 阻塞和非阻塞关注的是某个方法(方法其实内部包含更小粒度的方法)被调用后是否立刻返回,方法返回不等价于你要它完成的任务已经完成了。
- 你可能只是去营业厅取了个号,发现还没轮到你,你就回去继续做其它事情了。然后你就不管了吗?你得“轮询”时不时去营业厅看看是否轮到你办理业务啊,如果某次轮询发现到你了,于是你就把"实际处理业务"这件事情办理了,这才真正完成"办理业务"这件大事。
- 所以,你可以说"办理业务"是个"非阻塞"操作。
- 如果有的营业厅没有取号系统,那就意味着你得干等了,你得把"等待轮到你"和"实际处理业务"两件事情一起处理了,才能继续做其它事情。在有取号系统营业厅中,你有没有发现其实你可以决定"办理业务"这件事情是"阻塞"和"非阻塞"的。
- 同理,对于socket io,你可以设置为是"阻塞"还是"非阻塞"模式(文件io在操作系统层面上不支持非阻塞,所以编程语言也没有文件io的非阻塞实现)。
- 实际上,相同数据量下,socket io在"等待io就绪"这件事情上肯定比文件io要长的多,因为socket io的源头数据一般在远程另一台主机上,网络传输需要一些时间,而文件io源头数据在本机。
- 同步和异步关注的是"事情是否由调用者来做"。
- 在"办理业务"这件事情中,虽然细分为了"等待轮到你"和"实际处理业务"两件小事,但是当你时不时到营业厅
- 看是否轮到你,真正轮到你的时候,"实际处理业务"这件小事还是由你来做,这就得占用你的时间了,对于你来
- 说,"办理业务"这件事情还是得你来做,所以是"同步"操作;如果你将事情交给第二个人,说你帮我去营业厅办
- 点事,然后你自己去做其它事情,"办理业务"这件事情就变成异步操作了,你就只管问TA或者TA告诉你这件事
- 情完成没有。在IO中,就看"cpu将数据从内核空间拷贝到用户空间"这件事情是否由调用线程亲自执行,是说明
- 这是同步IO,如果交给其它线程执行,则是异步IO。
- 终于可以进入正题了,linux下的IO模型。
- 阻塞IO没什么可说的,是最基础的IO,这就像你去一个没取号系统的营业厅办事,只能干等,知道办理完,
- 才去做其它事情;或者你去一个有取号系统的营业厅,但是同样选择干等,直到处理完,因为你觉得你有大把
- 时间。
- java中的BIO就是基于阻塞IO的。阻塞IO其实都是同步的~
- 非阻塞IO就是类似有取号系统的营业厅,允许你在"等待就绪"的时候,先去做其它事情,相当于给你个及时反馈,类
- 似方法立即有返回,你时不时轮询,如果"就绪",在那次轮询时,就把"从内核空间拷贝数据到用户空间"的事情
- 做了,从而真正完成整件事情。发现没,非阻塞IO是同步的。
- IO多路复用,其实是非阻塞IO的增强,相当于你同时去多个有取号系统的营业厅办理业务,除此之外,不用你去
- 每个营业厅看看是否轮到你了,而是被动通知轮到你,就像轮到你的时候,给你手机发了条短信或者通过小程序
- 给你发了条消息,你就去对应营业厅办理对应的业务,所以IO多路复用又叫作事件驱动。linux下IO多路复用的
- 具体实现有selet(),poll(),epoll(),java中1.4之后的NIO,就是基于此的再次实现(windows系统也
- 是支持IO多路复用)。发现没,IO多路复用虽然非阻塞,但是还是同步操作。
- 注意的一个地方就是IO多路复用支持的是非阻塞IO,操作系统层面上,socket io是有非阻塞模式的,文件io
- 没有,至于为什么,相同数据量socket io的"等待就绪"时间比文件 io长,所以IO多路复用是针对socket io
- 而非文件io的,像redis服务端处理客户连接的处理就是基于此。
异步IO相当于调用线程将IO操作交给其它线程了,也就是你让其它人去营业厅办理业务。
使用较少,暂不多说。
- -END--
子非鱼:搞懂IOzhuanlan.zhihu.com原创不易,觉得本文有帮助,不妨点个赞和关注!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。