高性能IO底层原理

写在前面,拜读《Netty、Redis、Zookeeper高并发实战》的笔记以及思考

高性能IO底层原理

IO读写的基础原理

​ 用户程序进行IO的读写,依赖于底层的IO读写。都会用到read&write两大系统调用,不通操作系统中,IO读写系统调用的名称可能不完全相同,功能基本一致。在用户程序中,无论是Socket的IO还是文件IO操作都属于上层应用开发。

read系统调用:并不是直接从物理设备把数据读取到内存中,是把数据从内核缓冲区复制到进程缓冲区

write系统调用:也不是直接把数据写入到物理设备,是把数据从进程缓冲区复制到内核缓冲区

即上层应用无论是调用操作系统的read还是调用操作系统的write,都会涉及到缓冲区。
上层应用的IO操作,实际上不是物理设备级别的读写,而是缓存的复制。
read&write两大系统调用,都不负责数据在内核缓冲区和磁盘之间的交换。底层的读写交换是由操作系统内核来完成的。

内核缓冲区与进程缓冲区

​ 缓冲区的目的是为了减少频繁与设备之间的物理交换。设备的直接读写,涉及操作系统的中断。
当发生系统中断时,需要保存之前的进程数据和状态等信息,而结束中断之后,还需要恢复之前的进程数据和状态等信息。为了减少这种底层系统的时间损耗,性能损耗,就出行了内存缓冲区。

​ 有了内存缓冲区后,上层应用使用read系统调用时,仅仅是把数据从内核缓冲区复制到上层应用的缓冲区(进程缓冲区);上层应用使用write系统调用时,只需要把数据从进程缓冲区复制到内核缓冲区。底层操作会对内核缓冲区进行监控,等待缓冲区达到一定数量时,再进行IO设备的中断处理,集中执行物理设备的实际IO操作,这种机制提示了系统的性能。以至于什么时候中断(读中断,写中断),由操作系统的内核决定,用户程序不需要关心。

​ 从数量上来说,Linux系统中,操作系统内核只有一个内核缓冲区。
每个用户程序(进程),有自己的缓冲区,叫做进程缓冲区。
所以用户程序的IO读写程序,在大多数情况下,没有进行实际的IO操作,而是在进程缓冲区和内核缓冲区直接之间进行的数据交换。

典型的系统调用流程

​ 图示 系统调用read&write流程

image-20220426171219415

​ 例如read系统调用的输入流程

  • 等待数据就绪

  • 从内核向进程复制数据。

    如果read是一个socket。那么具体流程为:

    • 等待数据从网络中到达网卡,当等待的分组到达时,数据会被复制到内核中的某个缓冲区。

      (这个操作有操作系统完成,用户程序无感知)

    • 把数据从内核缓冲区复制到应用进程缓冲区

​ 从客户端和服务器端的角度来理解,即一次socket的请求和响应如下

  • 客户端请求: linux通过网卡读取到客户端的请求数据,将数据读取到内核缓冲区
  • 服务器端获取请求数据:通过read系统调用,从linux内核缓冲区读取数据,再送入Java进程缓冲区
  • 服务器端业务处理:Java进程在自己的用户空间处理客户端的请求
  • 服务器端返回数据:Java进程处理完成,构建好响应数据。通过write系统调用。将这些数据从用户缓存区写入内核缓冲区
  • 发送至客户端:linux内核通过网络IO,将内核缓冲区中的数据写入网卡,网卡通过底层的通信协议,会将数据发送给目标客户端。
四种主要的IO模型
  • 同步阻塞IO (Blocking IO)

    ​ 阻塞IO,指需要内核IO操作彻底完成后,才返回到用户空间执行用户的操作。

    阻塞指的是用户空间程序的执行状态,传统的IO模型都是同步阻塞IO。

    Java中默认创建的socket都是阻塞的。

    image-20220427164426056

  • 同步非阻塞IO (Non-blocking IO)

    ​ 非阻塞IO,指的是用户程序不需要等待内核IO操作彻底完成,可以立即返回执行用户操作,即处于非阻塞的状态。

    ​ 非阻塞IO要求socket被设置为NONBLOCK

    ​ 用户程序需要不断的进行IO系统调用,轮询数据是否已经准备好,制度完成IO系统调用为止。

    • 优点:每次发起的IO系统调用,在内核等待数据过程中可以立即返回,用户线程不会阻塞,实时性比较好。
    • 缺点:不断地轮询内核,占用大量的CPU时间,效率低下。

    image-20220427165052140

    阻塞和非阻塞

    ​ 阻塞是指用户空间(调用线程)一直等待,非阻塞指用户空间(调用线程)拿到内核返回的状态值就返回自己的空间。

  • IO多路复用(IO Multiplexing)

    ​ 避免同步非阻塞IO模型中轮询等待的问题,可以采用IO多路复用模型。

    ​ 在IO多路复用中,引入了一种新的系统调用,查询IO的就绪状态。在Linux系统中,对应的操作系统调用为select/epoll系统调用。通过该系统调用,一个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是内核缓冲区可读/可写),内存能过将就绪的状态返回给应用程序。随后,应用程序根据就绪的状态,进行对应IO系统调用。

    ​ 在IO多路复用模型中通过select/epoll系统调用,单个应用程序的进程,可以不断的轮询成百上千个的socket连接,当某个或者某些socket网络连接有IO就绪的状态,就返回对应的可执行的读写操作。

    image-20220427200959359

    例如发起一个多路复用IO的read读操作系统调用

    • 选择器注册

      先将read操作的目标socket网络连接,提前注册到select/epoll选择器中(Java中对应Selector类)

    • 就绪状态的轮询

      通过选择器的查询方法,查询所有注册过的socket连接的就绪状态。当其中任何一个socket连接数据准备好了,内核缓冲区有数据(就绪)了,内核就会将该socket连接维护为就绪状态。
      通过查询的系统调用,内核会返回一个就绪的socket列表。

      当用户程序执行select方法,整个线程就会阻塞掉

    • 用户线程发起read调用

      用户线程获取到就绪状态的socket列表后,依靠socket连接发起read系统调用,用户线程阻塞。内核开始复制数据,将数据从内存缓冲区复制到用户缓冲区。

    • 复制完成

      复制完成后,内核返回结果,用户线程接触阻塞状态。用户线程获取到了数据,继续向下执行。

    IO多路复用模型的特点
    • 操作系统的内核必须能够提供多路分离的系统调用select/epoll。

    • 多路复用也需要轮询,负责select/epoll查询调用的线程,需要不断的进行select/epoll轮询,进而

      获取到就绪的socket连接。

    • 优点: 与一个线程维护一个连接的阻塞IO模式相比,优势在于一个选择器查询的线程可以同时处理成千上万个连接(Connection)。系统不必创建,维护大量线程。减小系统开销。

    • 缺点: 本质上,select/epoll 系统调用也是阻塞式的,属于同步IO。需要等待读写事件就绪后,由系统调用本身进行读写。

  • 异步IO (Asynchronous IO)

    彻底解决线程的阻塞,需要引入异步IO模型。

    异步IO,指的是用户空间与内核空间的调用方式反过来,用户空间的线程编程被动接受者,而内核空间成为主动调用者。

    类似于Java中比较典型的回调模式,用户空间的线程向内核空间注册了各种IO时间的回调函数,由内核去主动调用。

image-20220428154042414

异步IO模型的流程

  • 当用户线程发起了read系统调用,立刻返回做其他事情,用户线程不会阻塞。
  • 内核开始准备数据,当数据准备好将数据从内核缓冲区复制到用户缓存区(用户空间的内存)
  • 内核会给用户线程发送一个信号(Signal)或回调用户线程注册的回调接口,通知用户线程read操作完成了。(所以有时也被称为信号驱动IO)
  • 用户线程读取用户缓冲区的数据,完成后续的业务操作。
特点

​ 应用程序只需要进行事件的注册与接收,其他工作留给了操作系统。因此需要底层内核提供支持。

注意

​ 理论上,异步IO是真正的异步输入输出,吞吐量高于IO多路复用模型。

但目前,Windows系统通过IOCP实现了真正的异步IO。在Linux系统下,异步IO模型在2.6版本才引入,

目前并不完善,底层实现仍使用epoll,与多路复用相同。netty框架使用的就是IO多路复用模型,而不是异步IO模型。

实战配置

文件句柄(文件描述符)

​ linux系统中,文件可分为:普通文件,目录文件,链接文件和设备文件。
文件描述符(File Descriptor)是内核为了高效管理已被打开的文件所创建的索引。是一个非负整数(通常是小整数),用于指代被打开的文件。所有的IO系统调用,包括socket的读写调用都是通过文件描述符完成的。

文件句柄数不够用时候,即单个进程打开的文件句柄数量,超过了系统配置的上限值,就会发出“Socket/File: Can’t open so many files”错误提示。对于高并发,高负债的应用,就必须调整这个系统参数。以适应处理并发大量连接的应用场景。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值