当前位置: 首页 > article >正文

Linux之【网络I/O】前世今生(一)

在 Linux之【磁盘I/O】前世今生 一文中,我们介绍了文件I/O 的细节。本文将继续介绍网络I/O的内容。

一、 基本概念

介绍网络I/O前,先了解一些基本概念。

1.1、上下文

CPU 寄存器,是CPU内置的小容量、速度极快的内存。程序计数器,是用来存储 CPU 正在执行的指令位置、或者即将执行的下一条指令位置。二者是 CPU 在运行任何任务时,必须依赖的环境,记为上下文

1.2、上下文切换

运行新的任务时需把当前任务的上下文(也就是CPU寄存器和程序计数器)保存起来,然后加载新任务的上下文到这些寄存器和程序计数器中,最后再跳转到程序计数器所指的新位置,运行新任务。

所以执行不同的程序,即进程切换时会发生上下文切换,线程切换也一样。

操作系统和用户程序是两个不同的程序,前者运行在内核态,后者运行在用户态,所以,内核态和用户态的切换一定会发生上下文切换,进程从用户态到内核态的转变,需要通过系统调用(调用操作系统)来完成。系统调用的过程,会发生CPU上下文的切换。

CPU 寄存器里原来用户态的指令位置,需要先保存起来。接着,为了执行内核态代码,CPU 寄存器需要更新为内核态指令的新位置。最后才是跳转到内核态运行内核任务。

1.3、Linux I/O读写方式

  • 轮询
  • 基于死循环对 I/O 端口进行不断检测;
  • 需要CPU参与。
  • I/O 中断
  • 当数据到达时,磁盘主动向 CPU 发起中断请求,由 CPU 自身负责数据的传输过程;
  • 需要CPU参与。
  • DMA 传输
  • DMA(Direct Memory Access,直接存储器访问) 传输则在 I/O 中断的基础上引入了 DMA 磁盘控制器,由 DMA 磁盘控制器负责数据的传输,降低了 I/O 中断操作对 CPU 资源的大量消耗;
  • DMA本质上是一块主板上独立的芯片,允许外设设备和内存存储器之间直接进行IO数据传输,其过程不需要CPU的参与;
  • 每个 I/O 设备里面都有自己的 DMA 控制器,如网卡、声卡、显卡、磁盘控制器等;
  • CPU 除了在数据传输开始和结束时做一点处理外(开始和结束时候要做中断处理),在传输过程中 CPU 可以继续进行其他的工作。这样在大部分时间里,CPU 计算和 I/O 操作都处于并行操作,使整个计算机系统的效率大大提高。

二、 网络I/O

2.1、传统网络I/O

以网络传输文件为例,需要经历步骤如下图所示:

在这里插入图片描述

read + write 组合:
  • 涉及上下文切换 4 次
    • read 调用发起,图示(1);
    • read 调用返回,图示(4);
    • write 调用发起,图示(5);
    • write 调用返回,图示(8);
  • 数据拷贝 4 次
    • 数据读取:
      • 磁盘拷贝到 page Cache,图示(2)DMA拷贝;
      • page Cache 拷贝到程序Buffer,图示(3)CPU拷贝;
    • 数据写入:
      • 程序Buffer 拷贝到Socket缓存区,图示(6)CPU拷贝;
      • Socket缓存区拷贝到网卡,图示(7)DMA拷贝;
  • 注意上图拷贝数据步骤(2)、(3)、(6)、(7)区别:(3)和(6)是CPU拷贝,(2)和(7)是DMA拷贝。

2.2、优化传统网络I/O

上文提到传统网络I/O性能最大的瓶颈在于:

  • 数据拷贝次数过多;
  • 上下文切换次数过多。

所以我们优化的主要方向就是这两个维度:减少数据拷贝次数和上下文切换次数。

2.2.1、减少数据拷贝次数

2.2.1.1、mmap(Memory Map,内存映射)

仔细观察,数据没有必要拷贝到程序 Buffer 中,我们可以采用 mmap(memory map,内存映射) 技术,将程序 Buffer 映射到内核 Page Cache,从而避免数据在内核态和用户态之间相互拷贝。

在这里插入图片描述

mmap + write 组合替代read + write 组合后:
  • 涉及上下文切换 4 次

    mmap 调用和返回、 write 调用和返回。

  • 数据拷贝 3 次
    • 1次CPU拷贝;
    • 2次DMA拷贝。

Java NIO有一个MappedByteBuffer的类,可以用来实现内存映射。它的底层是调用了Linux内核的mmap的API。

2.2.2、减少上下文切换次数

2.2.2.1、sendfile

目前为止,我们已经通过 mmap 技术成功的将数据拷贝次数减少了1次;但还是涉及4次上下文切换,我们可以将之前的两次系统调用(read、write)合并为一次,记为 sendfile(Linux 内核版本 2.1开始提供), sendfile 不会拷贝数据到用户态,直接在内核态工作。

在这里插入图片描述

sendfile 来替代 mmap + write 组合后:
  • 涉及上下文切换 2 次

    sendfile调用和返回;

  • 数据拷贝 3 次
    • 1次CPU拷贝;
    • 2次DMA拷贝。

java 中 FileChannel#transferTo() 底层调用的正是sendfile

2.2.2.2、sendfile + SG-DMA

linux 2.4版本之后,对sendfile做了优化升级,引入SG-DMA(需要硬件支持) 技术,其实就是对DMA拷贝加入了分散/收集(scatter/gather)操作,它可以直接从内核空间缓冲区中将数据读取到网卡。

SG-DMA:
CPU 将 Page Cache 中的数据描述信息(内存地址和偏移量)记录到socket缓冲区,由 SG-DMA 根据这些将数据从Page Cache拷贝到网卡,相比之前版本减少了一次CPU拷贝的过程。

在这里插入图片描述

sendfile + SG-DMA 组合:
  • 涉及上下文切换 2 次

    sendfile调用和返回;

  • 数据拷贝 2 次

    2次SG-DMA拷贝。

2.2.2.3、splice

sendfile 只适用于将数据从文件拷贝到 socket 套接字上,且sendfile + SG-DMA组合还需要硬件的支持,这也限定了它的使用范围。

Linux在 2.6.17 版本引入 splice 系统调用,不仅不需要硬件支持,同时还支持实现了两个普通文件之间数据流动;

也就是说 splice 不仅实现了sendfile + SG-DMA组合的功能,还扩大了使用范围。后续 sendfile 经过优化,底层也直接调用了 splice 来实现功能。

splice 系统调用可以在Page CacheSocket 缓存区之间建立管道(pipeline),从而避免了两者之间的CPU拷贝。

在这里插入图片描述

splice
  • 涉及上下文切换 2 次

    splice 调用和返回;

  • 数据拷贝 2 次

    2次DMA拷贝。

三、零拷贝(Zero-copy)

拷贝方式系统调用DMA拷贝次数CPU拷贝次数上下文切换次数
传统方式read + write224
内存映射mmap + write214
sendfilesendfile212
sendfile + SG-DMAsendfile202
splicesplice202

至此,我们将不断的将传统I/O进行改进,将CPU拷贝次数逐步降低为0。我们把改进后的拷贝方式,统称为零拷贝(Zero-copy),官方定义如下:

零拷贝是指计算机执行I/O操作时,CPU不需要将数据从一个存储区域复制到另一个存储区域,从而可以减少上下文切换以及CPU的拷贝时间。它是一种I/O操作优化技术。

从这个定义来看,在 Linux之【磁盘I/O】前世今生 中提到的 直接I/O (从硬盘直接拷贝数据到用户缓存,不需要OS Cache)同样属于零拷贝;此外,还有写时复制COW(copy on write) 同样也属于零拷贝

Linux 前世今生系列文章:

  • Linux之【文件系统】前世今生(一)
  • Linux之【文件系统】前世今生(二)
  • Linux之【内存管理】前世今生(一)
  • Linux之【磁盘 IO】前世今生
  • Linux之【网络I/O】前世今生(一)

http://www.kler.cn/a/541701.html

相关文章:

  • 【Qt之·类QTextCursor】
  • 中国通信企业协会 通信网络安全服务能力评定 证书使用说明
  • 【C#零基础从入门到精通】(九)——C#if和Switch判断语句详解
  • 使用Qt+opencv实现游戏辅助点击工具-以阴阳师为例
  • apache-poi导出excel数据
  • windows生成SSL的PFX格式证书
  • 【DeepSeek】deepseek可视化部署
  • C++ Type Traits介绍
  • 厘米和磅的转换关系
  • 【C#零基础从入门到精通】(六)——C#运算符
  • 服务器有多少线程?发起一个请求调用第三方服务,是新增加一个请求吗?如果服务器线程使用完了怎么办?
  • 玩转适配器模式
  • Python 数据结构速成教程
  • 西安电子科技大学考研成绩2月24号即可查询,成绩查询入口:
  • 基于单片机的并联均流电源设计(论文+源码)
  • 微信小程序案例3——仿香哈菜谱微信小程序
  • linux统计文件夹下有多少个.rst文件行数小于4行
  • 正式开启Django之旅
  • 25年重庆省考报名流程详细教程
  • 11.swagger使用
  • 在win11下配置QT存在问题
  • 使用 Notepad++ 编辑显示 MarkDown
  • c++标准模板库(stl)vector和String
  • 项目场景拷打
  • 人工智能浪潮下脑力劳动的变革与重塑:挑战、机遇与应对策略
  • ESM2和ESM fold:语言模型进行原子级结构预测