Java NIO学习系列五:I/O模型

  前面总结了不少IO、NIO相关的基础知识点,还总结了IO和NIO之间的区别及各自适用场景,本文会从另外一个视角来学习一下IO,即IO模型。什么是IO模型?对于不一样人、在不一样场景下给出的答案是不一样的,因此先限定一下本文的上下文:Linux环境下的network IO。linux

  本文会从以下几个方面展开:segmentfault

  一些基础概念缓存

  I/O模型安全

  总结网络

 

1. 一些基础概念

  IO模型这个概念属于比较基础的底层概念,在此以前容我再先简单介绍一些涉及到的更底层的概念,帮助对I/O模型的理解:异步

1.1 用户空间与内核空间

  如今操做系统都是采用虚拟存储器,对于32位操做系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方)。操做系统的核心是内核,独立于普通的应用程序,能够访问受保护的内存空间,也有访问底层硬件设备的全部权限。为了保证用户进程不能直接操做内核(kernel),保证内核的安全,操心系统将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间。针对linux操做系统而言,将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为内核空间,而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF),供各个进程使用,称为用户空间。socket

1.2 文件描述符

  对于内核而言,全部打开文件都由文件描述符引用。文件描述符是一个非负整数。当打开一个现存文件或建立一个新文件时,内核向进程返回一个文件描述符。当读、写一个文件时,用open或create返回的文件描述符标识该文件,将其做为参数传送给read或write。文件描述符这一律念每每只适用于UNIX、Linux这样的操做系统。async

1.3 缓存 I/O

  缓存I/O又被称做标准I/O,大多数文件系统的默认I/O操做都属于缓存I/O。在Linux的缓存I/O机制中,操做系统会将I/O的数据缓存在文件系统的页缓存(page cache)中,也就是说,数据会先被拷贝到操做系统内核的缓冲区中,而后才会从操做系统内核的缓冲区拷贝到应用程序的地址空间。函数

  由于数据在传输过程当中须要在应用程序地址空间和内核进行屡次数据拷贝操做,因此这些数据拷贝操做所带来的CPU以及内存开销是很是大的,这也是缓存I/O所带来的缺点。学习

 

2. I/O模型

  上面有提到,对于一次IO访问(好比read),数据会先被复制到操做系统内核缓冲区中,而后再从操做系统内核的缓冲区复制到应用程序的地址空间。也就是说,当一个IO操做发生时,会经历两个阶段:

  1. 数据准备阶段(Waiting for the data to be ready);
  2. 将数据从内核复制到进程中(Copying the data from the kernel to the process);

  这是由于存在上面两个阶段,linux系统产生了下面5种I/O模型:

  • 阻塞I/O(blocking IO)
  • 非阻塞I/O(nonblocking IO)
  • I/O多路复用(IO multiplexing)
  • 信号驱动I/O(signal driven IO)
  • 异步I/O(asynchronous IO)

  下面咱们来一一介绍(本文暂介绍除信号驱动I/O外其他4种IO模型)。

2.1 阻塞I/O(blocking IO)

  在linux中,默认状况下socket都是阻塞式的,一个典型的读操做流程大概是这样的:

  当用户进程调用recvfrom这个系统调用时,kernel就开始了IO的第一个阶段:数准备阶段(对于网络IO来讲,不少时候数据在一开始尚未到达。好比,尚未收到一个完整的TCP包。这个时候kernel就要等待直到全部数据到达),这个过程至关于将数据被复制到操做系统内核的缓冲区,是须要一个过程,须要等待的。而在用户进程这边,整个进程会被阻塞(固然,是进程本身选择的阻塞)。当kernel一直等到数据准备好了,它就会将数据从kernel中复制到用户内存,而后kernel返回结果,用户进程才会解除block的状态,从新运行起来。

  Blocking IO最大的特色就是在IO执行的两个阶段都被会阻塞。

2.2 非阻塞I/O(nonblocking IO) 

  能够将设置socket设置为non-blocking模式,对于此时的读操做,流程大概是这个样子的:

  当用户进程发起recvfrom这个系统调用时,若是kernel中的数据尚未准备好,那么它并不会block用户进程,而是马上返回一个error。从用户进程角度讲 ,它发起一个recvfrom调用,立刻就获得了一个结果,无论有没有数据。用户进程判断结果是一个error时,它就知道数据尚未准备好,因而它能够再次发起recvfrom操做。一旦kernel中的数据准备好了,而且又再次收到了用户进程的system call,那么它接下来就将数据复制到用户内存,而后返回。

  Nonblocking IO的特色是用户进程须要不断的主动询问kernel数据好了没有,这个过程不阻塞,可是在IO的第二个阶段仍是须要等待内核将数据复制到用户进程的,也就是这部分仍是会阻塞的。

2.3 I/O多路复用(IO multiplexing)

  IO multiplexing就是咱们说的IO多路复用,有些地方也称这种IO方式为event driven IO。主要是经过select/epoll来实现单个process同时处理多个网络链接的IO,它的基本原理是依赖select,poll,epoll这些function来不断的轮询负责的全部socket,当某个socket有数据到达了,就通知用户进程。

  当用户进程调用了select时整个进程会被block,同时kernel会“监视”全部select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操做,将数据从kernel复制到用户进程。

  因此,I/O多路复用的特色是经过一种机制使得一个进程能同时等待多个文件描述符(至于为何是文件描述符,由于对于kernel而言,全部打开文件都由文件描述符引用,而一次IO链接也至关于打开文件),而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就能够返回。

  IO多路复用是创建在Nonblocking IO之上的,即经过单个线程来“监视”多个IO链接,谁准备就绪了就处理谁,处理的过程和Nonblocking的过程是同样的。

2.4 异步I/O(asynchronous IO)

  Asynchronous IO不一样于上面三种模型,先看一下它的流程:

  用户进程发起read操做以后,马上就能够开始去作其它的事情了。另外一方面,从kernel的角度,当它收到一个asynchronous read调用以后,首先它会马上返回,因此不会对用户进程产生任何block。而后,kernel会等待数据准备完成,而且数据准备好以后再将数据复制到用户内存,当这一切都完成以后,kernel会给用户进程发送一个signal,告诉它read操做完成了。

  其实,从这里能够看出异步I/O最大的特色就是,将前面提到的两步IO操做所有异步化了,整个过程彻底不阻塞,而Nonblocking IO在复制数据到用户进程这一步其实仍是阻塞的,只是数据准备阶段不阻塞而已。

 

 3. 总结

  本文总结了linux网络IO中常见的五种IO模型,虽然说不是Java IO,可是Java IO、NIO中也是遵循一样的IO模型,关于这一点,后面会专门写一篇文章来阐述。

  五种IO模型分别为:阻塞I/O(blocking IO)、非阻塞I/O(nonblocking IO)、I/O多路复用(IO multiplexing)、信号驱动I/O(signal driven IO)、异步I/O(asynchronous IO)。在这五种模型的基础上,有两个概念不得不提,阻塞和非阻塞、同步和异步,这两个概念容易搞混,:

3.1 阻塞和非阻塞

  阻塞很好理解,就是进程或者线程停在那里等待某个状态,什么都不干。可是什么状况称为阻塞,什么状况称为非阻塞呢?在IO模型中的定义是取决于前面提到的第一阶段:数据准备阶段,也就是说,在这个阶段会致使进程或线程阻塞的IO就成为阻塞式IO,反之就是非阻塞式IO。因此Nonblocking IO在第一阶段是能够作别的事情的,可是在第二阶段任然是阻塞的,这点须要注意。

3.2 同步和异步

  在说明同步IO模型和异步IO模型的区别以前,须要先给出二者的定义。POSIX的定义是这样子的:

  • A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes;
  • An asynchronous I/O operation does not cause the requesting process to be blocked;

  二者的区别就在于执行"IO Operation"的时候是否会致使进程或线程阻塞,这个"IO Operation"是指前面提到的第二阶段:将数据从kernel复制到用户进程中。按照这个定义,前面说的Blocking IO、Non-blocking IO、IO multiplexing就都属于synchronous IO,只有异步IO才属于Asynchronous IO,由于只有它在整个IO过程当中都不会致使阻塞。

  最后再附上一张五种IO模型比较图,以帮助理解:

 

参考文献

Linux IO模式及 select、poll、epoll详解

相关文章
相关标签/搜索