IO模型简介

(一)IO的过程

      在linux 操做系统系统中几乎全部IO操做都是以“文件”的形式管理的(一切皆文件),对“文件”的读写通常都要通过内核态和用户态的切换,对于一次IO访问(以read为例),会经历两个阶段:linux

  1. 阶段一:调用操做系统的read方法,并开始阻塞等待,等待数据准备好(数据被拷贝到内核缓冲区);web

  2. 阶段二:将数据从内核缓冲区拷贝到应用进程,应用进程进行处理。数据库

 

(二)IO模型的演进(BIO、NIO、AIO)

      Java 中的 BIO、NIO和 AIO 本质上是 Java 语言对操做系统层面的各类 IO 模型的封装。windows

      BIO是JDK1.4以前的传统IO模型,属于阻塞模式,服务器采用每一个链接由独立线程维护的方式,即服务器收到客户端的链接请求时,会启动一个新的线程进行处理。在高并发场景下,机器资源很快会被耗尽,即便经过线程池来优化,也没法改变阻塞IO的根本问题,即每一个线程在IO执行的上述两个阶段都被阻塞,系统的吞吐量也天然很难提高。服务器

      NIO,能够支持非阻塞IO。NIO采用了多路复用的IO模式,多路复用的模式在Linux底层能够基于select、poll或epoll实现(本质都是一些系统函数),这种机制下能够用单个线程监视多个fd(文件描述符),当其中一个fd读写就绪,会通知用户线程进行IO操做。阻塞IO模式下阻塞的是每个用户线程,与之不一样的是,多路复用只须要阻塞一个用户线程便可,这个用户线程一般咱们叫它Selector,其实底层调用的是内核的select,只要任何一个IO操做就绪,就能够唤醒select,而后交由用户线程处理。用户线程读取数据这个过程仍然是阻塞的,多路复用技术只是在第一个阶段能够变为非阻塞调用,但在第二个阶段拷贝数据到用户空间,其实仍是阻塞的,多路复用技术的最大特色是使用一个线程就能够处理不少的socket链接,尽管性能上不必定提高,但并发能力和吞吐量却大大加强了。并发

       AIO,也被称为NIO2.0,在JDK1.7版本中发布,提供了AIO的功能,支持文件的异步IO操做。从NIO中能够看到,对于IO的两个阶段的阻塞,只是对于第一个阶段有所改善,对于第二个阶段在NIO里面仍然是阻塞的。而理想的异步非阻塞IO要作的就是,将IO操做的两个阶段都所有交给内核系统完成,用户线程只须要告诉内核,我要读取一块数据,请你帮我读取,读取完了放在我给你的地址里面,而后告诉我一声就能够了。从操做系统层面来看,AIO算是真正的实现了异步非阻塞,操做系统层面的异步须要系统原生提供支持,目前windows基于IOCP(nput/Output Completion Port)技术实现,在Linux上,目前有不少开源的异步IO库,例如libevent、libev、libuv,都不是基于操做系统的异步IO实现的,底层均是基于epoll实现的。异步

 

(三)UNIX IO模型解析

1)阻塞IO

       阻塞IO(blocking IO)模式下,用户进程在发起系统调用直到数据到达且被拷贝到用户空间的两个阶段都处于阻塞状态。socket

2)非阻塞IO

       非阻塞IO(noblocking IO)模式下,用户进程持续非阻塞地询问内核数据准备好了没有,若是数据还没准备好,用户进程不会被block住,内核会当即返回error给用户进程,直到数据准备就绪后,应用进程需同步等待数据从内核空间拷贝到用户空间。async

3)IO多路复用

      IO多路复用(IO multiplexing)模式也称做事件驱动IO。用户进程会阻塞在select(或poll、epoll)系统调用上,内核会不断的轮询所负责的全部socket(或其余文件描述符),当某个socket有数据到达了,就通知用户进程,select调用就返回,这时候用户进程再同步等待数据从内核空间拷贝到用户空间。函数

      IO多路复用和阻塞IO看起来貌似没有什么区别(两个阶段都是阻塞的),事实上还更差一些,由于这里须要使用两个系统调用(select和recvfrom),而阻塞IO只调用了一个系统调用(recvfrom)。可是,用select的优点在于它能够同时处理多个链接。(因此,若是处理的链接数不是很高的话,使用select/poll/epoll的web server不必定比使用multi-threading + blocking IO的web server性能更好,select/epoll的优点并非对于单个链接能处理得更快,而是在于能处理更多的链接。)

4)信号驱动IO

      信号驱动IO(signal-driven IO),使用信号机制,让内核在描述符就绪时发送SIGIO信号通知用户进程。整个过程是先经过sigaction系统调用安装一个信号处理函数。该系统调用将当即返回,用户进程继续工做,也就是说它没有被阻塞。当数据报准备好读取时,内核就为该进程产生一个SIGIO信号,咱们随后能够在信号处理函数中调用recvfrom读取内核空间准备好的数据。特色:第一阶段(等待数据报到达期间)进程不被阻塞。

5)异步IO

      异步IO(asynchronous IO)的工做机制是:告知内核开启某个操做,并让内核在整个操做完成后通知用户进程,两个阶段都不会被阻塞。

      与信号驱动IO模式的区别:信号驱动IO由内核通知咱们什么时候启动一个IO操做,异步IO由内核告诉咱们IO操做什么时候完成。

6)异步IO和同步IO

       前面说的5种IO模型的前四种,阻塞式I/O、非阻塞式I/O、I/O复用、信号驱动式I/O 在操做系统层面都是同步IO,它们都会阻塞在数据从内核空间复制到用户空间的缓冲区;异步IO模型在两个阶段都不会阻塞调用进程,在操做系统层面实现真正的异步IO。

 

(四)技术选型

       传统的服务器,IO模型是采用为每一个请求建立一个子线程来处理,这种模式在并发量小的状况下能够正常支撑业务,可是在高并发场景下,机器资源很快就会耗尽。现今常见的高吞吐高并发系统每每是基于事件驱动的IO多路复用模式设计,这种模式将全部的请求交给一个单独的线程管理,此线程被称之为事件循环线程,当事件等待的系统资源就绪时会及时进行处理,而不是为每一个链接生成一个OS线程。这种事件驱动的异步模型大幅度提高了服务器的吞吐能力,在相同配置的服务器能接受更多的并发请求。事件驱动模型的应用十分普遍,Redis就是一个典型的单线程基于事件驱动的内存数据库,Node.js、Nginx、Netty等也都是基于这种方式来实现高吞吐性能。

相关文章
相关标签/搜索