操作系统的I/O模型


在开始介绍NIO Reactor模式之前,先来介绍下操作系统的五种I/O模型,了解了这些模型,对理解java nio会有不小的帮助。

先来看下一个服务端处理一次网络请求的流程图:

图1

 一、图1解析

1.内核空间&用户空间

内核空间:操作系统运行时用于程序调度、虚拟内存的使用或者连接硬件资源的程序逻辑。

用户空间:应用程序能够申请使用的空间。

操作系统采用虚拟存储器,操作系统核心是内核(Kernel),独立于普通应用程序,它既可以访问受保护的内存空间,又有访问底层硬件设备的所有权限,为了保证内核安全,使得用户进程不直接操作内核,因此操作系统将虚拟存储器分为两个部分:内核空间&用户空间

2.网络请求流程

根据图1,客户端发起一个请求到服务端,请求首先到达的是服务端网卡(步骤1),然后将请求数据copy到内核空间的内核缓冲区内(步骤2),到这一步,我们说一个数据报已经准备好了。

用户空间里的web服务进程(我们真正的业务程序)发起读取内核缓冲区里的数据,若内核缓冲区准备好了数据报,则会将数据报由内核缓冲区copy到用户空间的web服务进程内(步骤3),然后拿着这些数据进行逻辑处理(步骤4),然后将处理结果copy到内和缓冲区(步骤5),然后内核缓冲区将该数据copy到网卡(步骤6),然后远程传输给客户端(步骤7),这就完成了一次网络请求-响应处理。

这里需要指出步骤3下面这一步,这一步没有计入步骤,但这一步恰好是理解I/O是否发生阻塞的关键,下面介绍阻塞/非阻塞IO时会详细讲。

3.套接字(socket)&文件描述符(fd)

TCP用主机的IP地址加上主机上的端口号作为TCP连接的端点,这种端点就叫做套接字(socket)。

文件描述符(fd),Unix/Linux系统下,其作为一个socket的句柄,可以看做是一个文件,在socket上收发数据,相当于对一个文件进行读写,所以一个socket句柄,通常也用表示文件句柄的fd来表示。

二、I/O模型

1.阻塞&非阻塞调用

阻塞与非阻塞的概念是针对调用方(一般指我们的业务程序,如图1中的web服务器进程)来说的。

阻塞调用:图1中步骤1、2执行期间,没有数据到达内核缓冲区,这个时候web服务器进程发起的获取数据的请求会被直接阻塞,当前相关线程会被挂起,直到步骤1、2完成,有数据写入内核缓冲区,这个时候才会唤醒线程执行步骤3和4.

非阻塞调用:与阻塞调用相反,当没有数据到达内核缓冲区时,web服务发起的获取数据的请求不会发生阻塞,相关线程可以选择做其他事情,然后轮询着查询请求结果即可,当某次轮询出结果,则进行步骤3和4的操作。

2.同步&异步处理

同步与异步的概念是针对被调用方(一般是指内核空间里的IO处理,如图1中的步骤1和2)来说的(一定要区分和理解阻塞/非阻塞、同步/异步这两个概念)。

同步处理:被调用方得到最终处理结果才返回给调用方。

异步处理:被调用方不用得到结果,只需返回一个状态给调用方,然后开始IO处理,处理完了就主动返回通知调用方。

3.数据输入的两个阶段

一个网络输入流程包含下面两个阶段:

①数据准备(步骤1、2)。

②将准备好的数据从内核空间复制到用户空间(步骤3)。

 

4.阻塞I/O模型

我们从图1的步骤3下面的那次请求开始画图,阻塞式I/O模型处理流程如下:

图2

从上图可以看出,阻塞IO模型是指从应用程序发起从socket获取数据(recvfrom)那一刻起,如果内核里没有准备好的数据报,则直接阻塞应用程序,导致应用程序无法去做别的任何事情,直到数据报准备好,被阻塞的程序才会被唤醒,继续处理下面拿到的数据报。

阻塞IO模型只允许一个线程处理一个连接请求,因此当并发量大的时候,会创建大量线程,线程切换开销很大,导致程序处理性能低下。具体参考BIO模式的服务端实现:SocketChannel与BIO服务器

5.非阻塞I/O模型

同样从应用程序发起获取数据的地方开始画图,非阻塞式I/O模型处理流程如下:

图3

从上图可以看出,非阻塞模式也是相对于调用者的,调用者在发送获取数据的请求时会将对应套接口设置为非阻塞,这样在数据报还未准备好的时候,应用程序就不会被阻塞了,然后应用程序再通过轮询的方式进行询问数据报是否已经准备好,当准备好后停止轮询,接下来的逻辑跟阻塞IO一致。对比可以发现,阻塞与非阻塞都是以调用方的角度看的,而且阻塞与否全在第一个阶段,第二个阶段都是一致的。非阻塞IO虽然不会阻塞应用程序,但是因为需要长时间的轮询,对于CPU来说,将会进行大量无意义的切换,资源利用率较低。

6.I/O多路复用模型

6.1.模型介绍

I/O多路复用模型处理流程如下:

图4

从上图可以看出,I/O多路复用,其实是找了个代理select,帮助监听多个I/O操作的状态,有新状态产生,才触发recvfrom操作,没有新的状态产生,则select会阻塞,注意这里的阻塞,与阻塞IO模型里的不同,阻塞IO模型是指一个IO操作发生的阻塞行为,而这里select可以同时阻塞多个IO操作,也就是说select可能会监听到一个以上的IO操作的状态,比如它可以同时对多个读、多个写的IO操作进行检测,直到有数据可读、可写时,才真正触发IO操作函数。

6.2.select、poll、epoll函数

上述三个函数均提供IO多路复用的解决方案,但是它们之间存在差异性,下面会介绍具体的区别:

select:

select 函数监视的fd分为writefds、readfds、exceptfds三类,调用后select函数会阻塞,直到有fd就绪(可读、可写、或except),或超时(指定等待时间,如果立即返回设为null即可),函数返回。当select函数返回后,可以通过遍历fdset,来找到已经就绪的fd。

优点:跨平台支持,目前几乎所有的平台都支持。

缺点:单个进程内其监视的fd存在最大限制,一般为1024个(linux32)或者2048个(linux64)。另外一个缺点就是其会不断的轮询fdset,不管存不存在活跃的socket,它都会全部遍历一遍fdset来查找就绪的fd,导致浪费许多CPU的时间去做这件事。最后一个缺点是其可能会维护一个存放大量fd的数据结构,这样会使用户空间和内核空间在传递该结构时复制开销过大。

poll:

本质上和select没有区别,但是它解决了select监视fd个数的限制。

优点:对于监视的fd,采用链表结构存储,无个数限制。

缺点:基本上select有的缺点它都有,其次它还有个特点:水平触发,也就是说poll到的fd没有被处理掉,下次依旧能被poll到。

select和poll一样,在大量客户端连接进来时,它们的效率会随着客户端数量而线性下降。

epoll:

Linux2.6开始支持的一个函数,是对select和poll的增强版本。

优点:没有监视fd个数的限制,主动通知(回调)机制,只关注活跃的fd,不用像select和poll那样全量遍历去找就绪的fd,因而也不存在随着客户端数量的增多而性能下降的问题。最后是内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递,即epoll使用mmap减少复制开销。

缺点:在大量客户端连接,并且大量活跃的fd时,其性能可能还不如select/poll。

7.信号驱动式I/O模型

信号驱动I/O模型处理流程如下:

图5

通过上图,在信号驱动 I/O 模型中,应用程序使用套接口进行信号驱动 I/O,并安装一个信号处理函数,进程继续运行并不会发生阻塞;

当数据准备好时,进程会收到一个 SIGIO 信号,可以在信号处理函数中调用 I/O 操作函数处理数据。

这种模式下在大量IO操作时可能会发生信号队列溢出而导致无法通知。在TCP下,该模式几乎没用,TCP下可通知的条件过多,每一个都进行判断会消耗掉大量的资源。

8.异步I/O模型(AIO)

上面介绍的几种I/O模型,对于IO处理本身而言,都是同步的,只有这个模型,针对IO处理本身来讲,是异步的。

下面来看看流程图:

图6

由上图看出,此模型下首先由应用程序告知内核启动某个操作,并让内核在整个操作包括将数据从内核拷贝到应用程序的缓冲区的过程中,完成后通知应用程序。

这跟上面的信号驱动IO模型有所不同,这个模型通知给应用程序时,IO操作已经全部完成,应用程序直接拿数据就好,无需再做任何IO操作(这就是此模型叫异步IO处理的原因),而信号驱动通常是返回给应用程序一个数据报准备状态,真正的IO操作仍需要应用程序进行。

目前AIO并不完善,最常用的高性能IO模型仍然是I/O多路复用模型。

三、总结

这几种模型除了AIO属于异步IO以外,其余的几种全都是同步IO(即需要应用程序主动进行IO操作),而是否阻塞应用程序取决于第一个阶段的处理方式,前几种IO模型的区别全在于第一阶段的处理。

本文参考:https://zhuanlan.zhihu.com/p/43933717

Java NIO学习与记录(五): 操作系统的I/O模型的更多相关文章

  1. Java NIO学习与记录(八): Reactor两种多线程模型的实现

    Reactor两种多线程模型的实现 注:本篇文章例子基于上一篇进行:Java NIO学习与记录(七): Reactor单线程模型的实现 紧接着上篇Reactor单线程模型的例子来,假设Handler的 ...

  2. Java NIO 学习笔记(五)----路径、文件和管道 Path/Files/Pipe

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  3. Java NIO学习与记录(六): NIO线程模型

    NIO线程模型 上一篇说的是基于操作系统的IO处理模型,那么这一篇来介绍下服务器端基于IO模型和自身线程的处理方式. 一.传统阻塞IO模型下的线程处理模式 这种处理模型是基于阻塞IO进行的,上一篇讲过 ...

  4. Java NIO学习与记录(二):FileChannel与Buffer用法与说明

    FileChannel与Buffer用法与说明 上一篇简单介绍了NIO,这一篇将介绍FileChannel结合Buffer的用法,主要介绍Buffer FileChannel的简单使用&Buf ...

  5. Java NIO学习与记录(一):初识NIO

    初识 工作中有些地方用到了netty,netty是一个NIO框架,对于NIO却不是那么熟悉,这个系列的文章是我在学习NIO时的一个记录,也期待自己可以更好的掌握NIO. 一.NIO是什么? 非阻塞式I ...

  6. Java NIO学习与记录(七): Reactor单线程模型的实现

    Reactor单线程模型的实现 一.Selector&Channel 写这个模型需要提前了解Selector以及Channel,之前记录过FileChannel,除此之外还有以下几种Chann ...

  7. Java NIO学习与记录(四): SocketChannel与BIO服务器

    SocketChannel与BIO服务器 SocketChannel可以创建连接TCP服务的客户端,用于为服务发送数据,SocketChannel的写操作和连接操作在非阻塞模式下不会发生阻塞,这篇文章 ...

  8. Java NIO学习与记录(三): Scatter&Gather介绍及使用

     Scatter&Gather介绍及使用 上一篇知道了Buffer的工作机制,以及FileChannel的简单用法,这一篇介绍下 Scatter&Gather 1.Scatter(分散 ...

  9. Java NIO 学习笔记(七)----NIO/IO 的对比和总结

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

随机推荐

  1. centos 7 安装最新版本git

    https://serverfault.com/questions/709433/install-a-newer-version-of-git-on-centos-7 You could use a  ...

  2. UVa 1025 A Spy in the Metro (DP动态规划)

    题意:一个间谍要从第一个车站到第n个车站去会见另一个,在是期间有n个车站,有来回的车站,让你在时间T内时到达n,并且等车时间最短, 也就是尽量多坐车,最后输出最少等待时间. 析:这个挺复杂,首先时间是 ...

  3. Thrift编译错误('::malloc' has not been declared)

    问题版本:0.9.0 make[4]: Entering directory `/tmp/X/thrift-0.9.0/lib/cpp' /bin/sh ../../libtool  --tag=CX ...

  4. PHP(三)运算符、流程控制和函数初步

  5. swift -懒加载创建view

     // 只有外界访问到headerView的时候才会去执行闭包, 然后将闭包的返回值赋值给headerView     // 注意: 一定要记住闭包后面需要写上(), 代表执行闭包     //懒加载 ...

  6. Python WebDriver 文件上传(一)

    昨天写了Web 文件下载的ui自动化,下载之后,今天就要写web 文件上传的功能了. 当然从折腾了俩小时才上传成功.下面写一下自己操作的步骤 首先网上说的有很多方法 如 input 标签的最好做了,直 ...

  7. handsontable-developer guide-cell editor

    单元格编辑 cell editor renderer:展示数据:editor:改变数据:renderer用一个函数表示:后者有一系列的操作,需要用class来表示: EditorManager han ...

  8. Android-HttpUtil工具类

    Http(Java 版 HttpURLConnection)请求的相关工具类 public class HttpUtil { private static final int TIMEOUT_IN_M ...

  9. PostgreSQL 表空间

    PostgreSQL 表空间 一 介绍使用表空间可以将不同的表放到不同的存储介质或不同的文件系统下,实际上是为表指定一个存储的目录.创建数据库,表,索引时可以指定表空间,将数据库,表,索引放到指定的目 ...

  10. C# 图书整理

    C#测试驱动开发C#设计模式C#高级编程单元测试之道C#版:使用Nunit 继续添加......