IO就是输入和输出。
首先从计算机结构的角度解读IO。
冯诺依曼结构,计算机分为五大部分:运算器、控制器、存储器、输入设备、输出设备。
从计算机结构的视角来看的话,IO描述了计算机系统与外部设备之间通信的过程。
再从应用程序角度解读IO。
操作系统中,为了保证操作系统的稳定性和安全性,一个进程的地址空间划分为用户空间和内核空间。
像平常运行的应用程序都是运行在用户空间,只有内核空间才能进行系统级别的资源相关操作,比如文件管理,进程通信,内存管理等。也就是说想进行IO操作,一定要依赖内核空间的能力。
并且,用户空间的程序不能直接访问内核空间。
当想要执行IO操作时,由于没有执行这些操作的权限,只能发起系统调用请求操作系统帮忙完成。
因此,用户进程想要执行IO操作的话,必须通过系统调用来间接访问内核空间。
平时开发过程中接触最多的就是磁盘IO和网络IO。
从应用程序的视角来看的话,我们应用程序对操作系统的内核发起IO调用(系统调用),操作系统负责的内核执行具体的IO操作。也就是说,我们的应用程序实际上只是发起了IO操作的调用而已,具体IO执行是由操作系统的内核来完成的。
当应用程序发起IO调用后,会经历两个步骤:
1、内核等待IO设备准备好数据
2、内核将数据从内核空间拷贝到用户空间
有哪些常见的IO模型?
unix系统下,IO模型一共有五种:同步阻塞IO、同步非阻塞IO、IO多路复用、信号驱动IO和异步IO。
Java中常见的三种IO模型
BIO:同步阻塞IO模型。
同步阻塞IO模型中,应用程序发起read调用后,会一直阻塞,直到内核把数据拷贝到用户空间。
NIO:java中把NIO可以看作是IO多路复用模型。也有很多人认为Java中NIO属于同步非阻塞IO模型。
同步非阻塞IO模型中,应用程序会一直发起read调用,等待数据从内核空间拷贝到用户空间的这段时间里,线程依然是阻塞的,直到在内核把数据拷贝到用户空间。
相比于同步阻塞IO模型,同步非阻塞IO,有了很大的改进。通过轮询操作,避免了一直阻塞。
但是这种IO模型同样存在问题:应用程序不断进行IO系统调用轮询数据的过程是十分消耗cpu资源的。
这是IO多路复用模型就上场了。
IO多路复用模型中,线程首先发起select调用,询问内核数据是否准备就绪,等内核把数据准备好了,用户线程再发起read调用。read调用的过程(数据从内核空间——>用户空间)还是阻塞的。
目前支持IO多路复用的系统调用,有select,epoll等等。select系统调用,是目前几乎在所有的操作系统上都有支持
select调用:内核提供的系统调用,它支持一次查询多个系统调用的可用状态。几乎所有的操作系统上都有支持。
epoll调用:linux2.6内核,属于select调用的增强版本。优化了IO的执行效率。
IO多路复用模型。通过减少无效的系统调用,减少了对CPU资源的消耗。
Java中的NIO,有一个非常重要的选择器(Selector)的概念,也可以被称为多路复用器。通过它,只需要一个线程便可以管理多个客户端连接。当客户端数据到了之后,才会为其服务。
AIO也就是NIO2。java7中引入了NIO的改进版本NIO2,他是异步IO模型。
异步IO是基于事件和回调机制实现的,也就是应用操作之后会直接返回。不会阻塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。
AIO应用不是很广泛。netty之前尝试使用过AIO,不过因为Netty使用AIO后,在Linux系统上性能并没有多少提升。
总结:
BIO:阻塞等待直到处理完成
NIO:通过选择器监听多个通道,非阻塞,处理完成之后就返回。
AIO:通知回调;