前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >IO模型

IO模型

原创
作者头像
羽毛球初学者
发布2024-10-14 16:34:48
660
发布2024-10-14 16:34:48
举报
文章被收录于专栏:JAVA基础知识

在了解IO模型前,需要先知道部分操作系统的概念。

内存空间 & 用户空间

应用程序需要经过操作系统,才能做一些特殊操作,如磁盘读写。此类操作有较大风险,只能交给操作系统来控制。因此,操作系统将进程占用的内存空间划分为两部分:用户空间和内核空间。内核空间是操作系统内核访问的、受保护的内存空间,用户空间则是用户应用程序访问的内存区域。

用户态 & 内核态 & CPU切换

如果应用程序需要使用到内核空间的资源,则需要通过系统调用来完成,也就是 CPU 要进行用户态和内核态的切换。

  • 进程运行于内核空间,称为进程的内核态
  • 进程运行于用户空间,称为进程的用户态

进程从用户态到内核态的转变,需要通过系统调用来完成。系统调用的过程,会发生CPU上下文的切换。CPU上下文切换就是先把前一个任务的CPU上下文(也就是CPU寄存器和程序计数器)保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务。

虚拟内存

现代操作系统使用虚拟内存,即虚拟地址取代物理地址,使用虚拟内存可以有2个好处:

  • 虚拟内存空间可以远远大于物理内存空间
  • 多个虚拟内存可以指向同一个物理地址

正是多个虚拟内存可以指向同一个物理地址,可以把内核空间和用户空间的虚拟地址映射到同一个物理地址,这样的话,就可以减少IO的数据拷贝次数。

虚拟内存
虚拟内存

DMA

DMA,英文全称是Direct Memory Access,即直接内存访问。DMA 本质上是一块主板上独立的芯片,允许外设设备和内存存储器之间直接进行IO数据传输,不需要CPU参与。

DMA 的工作流程为:

  • 用户应用进程调用read函数,向操作系统发起 IO 调用,进入阻塞状态,等待数据返回;
  • CPU 收到指令后,对 DMA 控制器发起指令调度;
  • DMA 收到 IO 请求后,将请求发送给磁盘;
  • 磁盘将数据放入磁盘控制缓冲区,并通知 DMA;
  • DMA 将数据从磁盘控制器缓冲区拷贝到内核缓冲区;
  • DMA 向 CPU 发出数据读完的信号,把工作交换给 CPU,由 CPU 负责将数据从内核缓冲区拷贝到用户缓冲区;
  • 用户应用进程由内核态切换回用户态,解除阻塞状态。

DMA 的主要作用就是将数据从磁盘拷贝到内核缓冲区,这期间不需要占用 CPU 资源。

有了以上概念,下面就可以进一步了解 IO模型了。

同步阻塞IO(BIO)

BIO 为同步阻塞 IO,blocking queue 的简写,也就是说多线程情况下只有一个线程操作内核的 queue,当前线程操作完 queue后,才能给下一个线程操作。在 BIO 下,一个连接就对应一个线程,如果连接特别多的情况下,就会有特别多的线程,很费资源。

同步非阻塞IO(NIO)

Non-blocking IO的简写,同步非阻塞IO,内核发生了变化,应用程序访问内核的缓冲区时不会阻塞,但是返回值需要用户自己判断;如果连接数特别多的情况下,就需要应用程序不停遍历,一个个进行状态的判断,询问是否有数据到达。当线程未读取到任何数据,线程需要不断地发起IO请求,直到数据到达后,才真正读取到数据,继续执行。

BIO流程示例图
BIO流程示例图

IO多路复用

操作系统提供了一类函数(select、poll、epoll等),它们可以同时监控多个fd(文件描述符)。当任何一个 fd 返回内核数据就绪,应用进程再发起 recvfrom 系统调用去读取数据。这也是IO多路复用的核心思路。

select

IO多路复用-select流程图
IO多路复用-select流程图

但是使用select方式有明显缺点:

  • 监听的IO最大连接数有限,在Linux系统上一般为1024;
  • select 函数返回后,是通过遍历fdset,找到就绪的描述符fd。(仅知道有I/O事件发生,却不知是哪几个流,所以遍历所有流)

pool

因为 select 方式存在连接数限制,所以后来又提出了 poll。与 select 相比,poll 解决了连接数限制问题。但是,select 和 poll 一样,还是需要通过遍历文件描述符来获取已经就绪的 socket。如果同时连接的大量客户端,在一时刻可能只有极少处于就绪状态,伴随着监视的描述符数量的增长,效率也会线性下降。因此经典的多路复用模型 epoll 诞生。

epoll

为了解决 select/poll 存在的问题,多路复用模型 epoll 诞生。它采用事件驱动来实现,epoll 先通过 epoll_ctl() 来注册一个 fd,一旦基于某个 fd 就绪时,内核会采用回调机制,迅速激活这个 fd,当进程调用 epoll_wait() 时便得到通知。这里去掉遍历文件描述符的操作,而是采用监听事件回调的机制。

IO多路复用-epoll
IO多路复用-epoll

事件驱动模型

信号驱动IO不再用主动询问的方式去确认数据是否就绪,而是向内核发送一个信号(调用 sigaction 的时候建立一个 SIGIO 的信号),然后应用用户进程可以去做别的事,不用阻塞。当内核数据准备好后,再通过 SIGIO 信号通知应用进程,数据准备好后的可读状态。应用用户进程收到信号之后,立即调用recvfrom,去读取数据。

事件驱动流程图
事件驱动流程图

信号驱动IO模型,在应用进程发出信号后,是立即返回的,不会阻塞进程。但是当数据复制到应用缓冲的时候,应用进程还是阻塞的。回过头来看下,不管是BIO,还是NIO,还是信号驱动,在数据从内核复制到应用缓冲的时候,都是阻塞的。基于这点,Java7 引入了AIO。

异步非阻塞IO(AIO)

AIO 主要是用来解决数据复制阶段的阻塞问题。在进行读写操作时,线程不必等待结果,而是将来由操作系统来通过回调方式,由另外的线程来获得结果。

AIO流程
AIO流程

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 内存空间 & 用户空间
  • 用户态 & 内核态 & CPU切换
  • 虚拟内存
  • DMA
  • 同步阻塞IO(BIO)
  • 同步非阻塞IO(NIO)
  • IO多路复用
    • select
      • pool
        • epoll
        • 事件驱动模型
        • 异步非阻塞IO(AIO)
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档