1️⃣ 传统 I/O 过程

在没有 DMA 技术之前,I/O 过程如下所述 (以 read 举例):

  • 用户执行系统调用 read:将控制权从 用户态 转换到 内核态,此时进程阻塞在这里。
  • CPU 向磁盘发送 I/O 请求:磁盘接收到 I/O 请求后,就需要的数据填充到自己的磁盘缓冲区,然后产生中断信号。
  • 磁盘向 CPU 发送 I/O 中断信号:
    • CPU 先就把磁盘缓冲区的数据一次一个字节的读进到自己的 寄存器
    • 再把 寄存器的数据 写入到 内存 中,但是传输过程中 CPU 无法执行其他任务。
  • 系统调用 read 返回:将控制权从 内核态 转换到 用户态

2️⃣ DMA 技术

定义:

  在进行 I/O 设备和内存的数据传输的时候,数据搬运的工作全部交给 DMA 控制器,而 CPU 不再参与任何与数据搬运相关的事情,这样 CPU 就可以去处理别的事务。

  • 用户执行系统调用 read:将控制权从 用户态 转换到 内核态,此时进程还是会阻塞在这里。
  • CPUDMA 发送 I/O 请求:此时 CPU 就可以执行其他任务,搬运工作就交给 DMA
  • DMA 再将 I/O 请求发送给磁盘:磁盘接收到 I/O 请求后,就需要的数据填充到自己的磁盘缓冲区,然后产生中断信号。
  • 磁盘向 DMA 发送 I/O 中断信号:
    • DMA磁盘控制器缓冲区 中的数据拷贝到 内核缓冲区 中,此时不占用 CPU
  • DMA 再向 CPU 发送 I/O 中断信号:
    • CPU 再将 内核缓冲区 的数据拷贝到 用户缓冲区
  • 系统调用 read 返回:将控制权从 内核态 转换到 用户态

3️⃣ 传统文件传输

四次拷贝 + 四次切换

read + write: 先将磁盘上的文件读取出来,然后通过网络协议发送给客户端。

4️⃣ 零拷贝

定义:不在内存层面去拷贝数据,也就是说全程没有通过 CPU 来搬运数据,所有的数据都是通过 DMA 来进行传输的。

作用:优化文件传输的性能

目的:减少「上下文切换」和「数据拷贝」的次数。

方法:总共分为两种

方法一:mmap + write(三次拷贝 + 四次切换)

可知,在 read 系统调用时:会将 内核缓冲区 的内容拷贝到 用户缓冲区

  为了减少此处的开销,可以直接使用 mmap 代替 read ,这使得 进程操作系统 共享内核缓冲区。

mmap() 会将 内核缓冲区 的内容直接 映射到 用户缓冲区,减少一次拷贝的。

方法二:sendfile(三次拷贝 + 两次切换)

sendfile 可以直接将 内核缓冲区 里的数据拷贝到 socket 缓冲区