• IO 原理


    【作用】

    Netty之所以受青睐,是因为Netty提供异步的、事件驱动的网络应用程序框架和工具。 作为一个异步框架,Netty的所有IO操作都是异步非阻塞的,通过Future- Listener机制, 用户可以方便地主动获取或者通过通知机制获得IO操作结果

    【io读写原理】

    1.大家知道,用户程序进行IO的读写,依赖于底层的IO读写,基本上会用到底层的read&write两大系统调用。
    在不同的操作系统中,IO读写的系统调用的名称可能不完全一样,但是基本功能是一样的。
    2.这里涉及一个基础的知识: read系统调用,并不是直接从物理设备把数据读取 到内存中;write系统调用,也不是直接把内存数据写入到物理设备
    上层应用无论是调用操作系统的read,还是调用操作系统的write,都会涉及缓冲区。
    具体来说, 调用 操作系统的read,是把数据从内核缓冲区复制到进程缓冲区;而write系统调用,是 把数据从进程缓冲区复制到内核缓冲区
    3.也就是说, 上层程序的IO操作,实际上不是物理设备级别的读写,而是缓存的 复制
    read&write两大系统调用,都不负责数据在内核缓冲区和物理设备(如磁盘)之间的交换, 这项底层的读写交换,是由操作系统内核(Kernel)来完成的。
    注:本书后文如果没有特别指明,内核即指操作系统内核。
    4.在用户程序中,无论是Socket的IO、还是文件IO操作,都属于上层应用的开发,它们的输入(Input)和输出(Output)的处理,在编程的流程上,都是一致的。

    【内核缓冲/进程缓冲】

    为什么设置那么多的缓冲区,为什么要那么麻烦呢? 缓冲区的目的,是为了减 少频繁地与设备之间的物理交换。其实和数据库的page缓存落盘到持久化存储一个道理。
    大家都知道,外部设备的直接读写,涉及操作系 统的中断。发生系统中断时,需要保存之前的进程数据和状态等信息,而结束中断 之后,还需要恢复之前的进程数据和状态等信息。
    为了减少这种底层系统的时间损 耗、性能损耗,于是出现了内存缓冲区
    有了内存缓冲区,上层应用使用read系统调用时,仅仅把数据从内核缓冲区复 制到上层应用的缓冲区(进程缓冲区);.
    上层应用使用write系统调用时,仅仅把数 据从进程缓冲区复制到内核缓冲区中。
    底层操作会对内核缓冲区进行监控,等待缓 冲区达到一定数量的时候,再进行IO设备的中断处理,集中执行物理设备的实际IO 操作,这种机制提升了系统的性能。
    至于什么时候中断(读中断、写中断),由操 作系统的内核来决定,用户程序则不需要关心。
    从数量上来说,在Linux系统中, 操作系统内核只有一个内核缓冲区。而每个用户程序(进程),有自己独立的缓冲区,叫作进程缓冲区。所以,用户程序的IO读写程序,在大多数情况下,并没有进行实际的IO操作,而是在进程缓冲区和内核缓冲区之间直接进行数据的交换。

    【接触linux句柄限制】

    文件句柄,也叫文件描述符。在Linux系统中,文件可分为:普通文件、目录文件、链接文件和设备文件。文件描述符(File Descriptor)是内核为了高效管理已被打开的文件所创建的索引,它是一个非负整数(通常是小整数),用于指代被打开的文件。所有的IO系统调用,包括socket的读写调用,都是通过文件描述符完成的。在Linux下,通过调用ulimit命令,可以看到单个进程能够打开的最大文件句柄数量,这个命令的具体使用方法是:
    ulimit -n

    【4种io模型】

    1.同步阻塞IO(Blocking IO)

    在阻塞式IO模型中, Java应用程序从IO系统调用开始,直到系统调用返回,在 这段时间内,Java进程是阻塞的。返回成功后,应用进程开始处理用户空间的缓存区数据。
    举个例子,在Java中发起一个socket的read读操作的系统调用,流程大致如下:
    (1) 从Java启动IO读的read系统调用开始,用户线程就进入阻塞状态
    (2)当系统内核收到read系统调用,就开始准备数据。一开始,数据可能还没有到达内核缓冲区(例如,还没有收到一个完整的socket数据包),这个时候内核就要等待。
    (3)内核一直等到完整的数据到达,就会将数据从内核缓冲区复制到用户缓冲区(用户空间的内存),然后内核返回结果(例如返回复制到用户缓冲区中的字节数)。
    (4)直到内核返回后,用户线程才会解除阻塞的状态,重新运行起来。总之,阻塞IO的特点是:在内核进行IO执行的两个阶段,用户线程都被阻塞了。
    阻塞IO的优点是: 应用的程序开发非常简单;在阻塞等待数据期间,用户线程 挂起。在阻塞期间,用户线程基本不会占用CPU资源
    阻塞IO的缺点是: 一般情况下,会为每个连接配备一个独立的线程;反过来 说,就是一个线程维护一个连接的IO操作。在并发量小的情况下,这样做没有什么问题。但是,当在高并发的应用场景下,需要大量的线程来维护大量的网络连接,内存、线程切换开销会非常巨大。因此,基本上阻塞IO模型在高并发应用场景下是不可用的。

    2.同步非阻塞NIO(None Blocking IO)

    socket连接默认是阻塞模式,在Linux系统下,可以通过设置将socket变成为非阻塞的模式(Non-Blocking)。使用非阻塞模式的IO读写,叫作同步非阻塞IO(None Blocking IO),简称为NIO模式。在NIO模型中,应用程序一旦开始IO系统调用,会出现以下两种情况:
    (1) 在内核缓冲区中没有数据的情况下,系统调用会立即返回,返回一个调 用失败的信息。后面会继续重复调用直到有数据
    (2) 在内核缓冲区中有数据的情况下,是阻塞的,直到数据从内核缓冲复制 到用户进程缓冲。复制完成后,系统调用返回成功,应用进程开始处理用户空间的 缓存数据
    注:不同于java中的额NIO(new io)。 可以发现情况相比于同步阻塞[类比于一直等待],同步非阻塞[类比于多次轮询]
    举个例子。发起一个非阻塞socket的read读操作的系统调用,流程如下:
    (1)在内核数据没有准备好的阶段,用户线程发起IO请求时,立即返回。 以,为了读取到最终的数据,用户线程需要不断地发起IO系统调用
    (2)内核数据到达后,用户线程发起系统调用,用户线程阻塞。内核开始复制数据,它会将数据从内核缓冲区复制到用户缓冲区(用户空间的内存),然后内核返回结果(例如返回复制到的用户缓冲区的字节数)。
    (3)用户线程读到数据后,才会解除阻塞状态,重新运行起来。也就是说,用户进程需要经过多次的尝试,才能保证最终真正读到数据,而后继续执行。同步非阻塞IO的特点:应用程序的线程需要不断地进行IO系统调用,轮询数据是否已经准备好,如果没有准备好,就继续轮询,直到完成IO系统调用为止。同步非阻塞IO的优点:每次发起的IO系统调用,在内核等待数据过程中可以立即返回。用户线程不会阻塞,实时性较好。 同步非阻塞IO的缺点:不断地轮询内核,这将占用大量的CPU时间,效率低
    总体来说,在高并发应用场景下,同步非阻塞IO也是不可用的。一般Web服务器不使用这种IO模型。这种IO模型一般很少直接使用,而是在其他IO模型中使用非阻塞IO这一特性。在Java的实际开发中,也不会涉及这种IO模型。这里说明一下,同步非阻塞IO,可以简称为NIO,但是,它不是Java中的NIO,虽然它们的英文缩写一样,希望大家不要混淆。
    为了避免NIO的轮询带来的cpu性能开销,Java的NIO(New IO), 叫作 IO多路复用模型(IO Multiplexing)出来了。

    3.IO多路复用模型(IO Multiplexing)

    NIO的每个发起read请求的线程自己去进行数据是否就绪的轮询,而IO多路复用是专门找了一个线程去帮其他所有的read请求轮询数据是否就绪
    在IO多路复用模型中, 引入了一种新的系统调用,查询IO的就绪状态[linux中是select/epoll]。通过该系统调用,一个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是内核缓冲区可读/可写),内核能够将就绪的状态返回给应用程序。随后,应用程序根据就绪的状态,进行相应的IO系统调用。
    目前支持IO多路复用的系统调用,有select、epoll等等。select系统调用,几乎在所有的操作系统上都有支持,具有良好的跨平台特性。epoll是在Linux 2.6内核中提出的,是select系统调用的Linux增强版本。
    在IO多路复用模型中通过select/epoll系统调用,单个应用程序的线程,可以不 断地轮询成百上千的socket连接,当某个或者某些socket网络连接有IO就绪的状态,就返回对应的可以执行的读写操作。
    通过拔开关的方式,实现分时复用:
    和NIO模型相似,多路复用IO也需要轮询。 负责select/epoll状态查询调用的线 程,需要不断地进行select/epoll轮询,查找出达到IO操作就绪的socket连接。IO多路复用模型与同步非阻塞IO模型是有密切关系的。 对于注册在选择器上的 每一个可以查询的socket连接,一般都设置成为同步非阻塞模型。仅是这一点,对于用户程序而言是无感知的。
    IO多路复用模型的优点:与一个线程维护一个连接的阻塞IO模式相比,使用select/epoll的最大优势在于,一个选择器查询线程可以同时处理成千上万个连接(Connection)。系统不必创建大量的线程,也不必维护这些线程,从而大大减小了系统的开销。
    Java语言的NIO(New IO)技术,使用的就是IO多路复用模型。在Linux系统上,使用的是epoll系统调用。
    IO多路复用模型的缺点: 本质上,select/epoll系统调用是阻塞式的,属于同步 IO。都需要在读写事件就绪后,由系统调用本身负责进行读写,也就是说这个读写 过程是阻塞的
    如何彻底地解除线程的阻塞,就必须使用异步IO模型。

    4.异步IO模型(Asynchronous IO)

    异步IO模型(Asynchronous IO,简称为AIO)。AIO的基本流程是: 用户线程 通过系统调用,向内核注册某个IO操作。内核在整个IO操作(包括数据准备、数据 复制)完成后,通知用户程序,用户执行后续的业务操作
    在异步IO模型中,在整个内核的数据处理过程中,包括内核将数据从网络物理设备(网卡)读取到内核缓冲区、将内核缓冲区的数据复制到用户缓冲区,用户程序都不需要阻塞。
    异步IO模型的特点:在内核等待数据和复制数据的两个阶段,用户线程都不是阻塞的。 用户线程需要接收内核的IO操作完成的事件,或者用户线程需要注册一个 IO操作完成的回调函数。正因为如此,异步IO有的时候也被称为信号驱动IO
    异步IO异步模型的缺点: 应用程序仅需要进行事件的注册与接收,其余的工作 都留给了操作系统,也就是说,需要底层内核提供支持。 理论上来说,异步IO是真正的异步输入输出,它的吞吐量高于IO多路复用模型 的吞吐量
    就目前而言,Windows系统下通过IOCP实现了真正的异步IO。
    而在Linux系统 下,异步IO模型在2.6版本才引入,目前并不完善,其底层实现仍使用epoll,与IO多 路复用相同,因此在性能上没有明显的优势。
    大多数的高并发服务器端的程序,一般都是基于Linux系统的。因而,目前这类 高并发网络应用程序的开发,大多采用IO多路复用模型。 大名鼎鼎的Netty框架,使用的就是IO多路复用模型,而不是异步IO模型
  • 相关阅读:
    Oracle SQL 核心
    SpringBoot 读取项目中静态资源文件
    【数据库优化方案】后端思维--数据库性能优化方案
    前端都应该要掌握的防抖和节流
    自5月以来,俄罗斯Sandworm黑客侵入了11家乌克兰电信公司
    数据结构和算法是人工智能的基石
    Vue路由实例
    OneNote Win10自带的宝藏笔记工具
    java计算机毕业设计智能停车场管理系统源程序+mysql+系统+lw文档+远程调试
    LabVIEW中管理大型数据
  • 原文地址:https://blog.csdn.net/qq_34448345/article/details/127440328