[Netty学习笔记]五、零拷贝

零拷贝

用户态和内核态概念

对于任何操作系统来说,创建一个进程是核心功能。创建进程要做很多工作,会消耗很多物理资源。比如分配物理内存,父子进程拷贝信息等,这些工作得由特定的进程去做,所以有了特权级别的概念。最关键的工作必须交给特权级别最高的进程去执行,这样可以做到集中管理,减少有限资源的访问和使用冲突。Intel x86架构的CPU一共有四个级别,0-3级,0级特权级最高,3级特权级最低。

当一个进程在执行用户自己的代码时处于用户运行态(用户态),此时特权级最低,为3级,是普通的用户进程运行的特权级,大部分用户直接面对的程序都是运行在用户态。并且3级状态不能访问0级的地址空间,包括代码和数据。当一个进程因为系统调用进入内核代码中执行时处于内核运行态,此时特权级别最高,为0级。执行的内核代码会使用当前进程的内核栈,每个进程都有自己的内核栈。

用户运行一个程序,该程序创建的线程开始时运行自己的代码,处于用户态。如果要执行文件操作、网络数据发送等操作必须通过write/send等系统调用,这些系统调用会调用内核的代码。进程会切换到内核态去执行内核代码来完成相应操作。内核态的进程执行完后又会切换到用户态。这样用户态的程序就不能随意操作内核地址空间,具有一定的安全保护作用。

传统IO读写
File file=new File("1.txt");
RandomAccessFile raf=new RandomAccessFile(file,"rw");
byte[] arr=new byte[(int)file.length()];
raf.read(arr);

Socket socket=new ServerSocket(8090).accept();
socket.getOutPutStream().write(arr);

上面给出的例子是从磁盘读取文件并且通过socket写出去的过程

在这里插入图片描述
图解说明:

  1. 程序使用read()进行系统调用,系统由用户态转为内核态,磁盘中的数据由DMA(Direct Memory Access直接内存访问,CPU不参与数据的读写,直接将数据读取到内存中)的方式将数据读取到内核缓冲区kernel buffer。
  2. 系统由内核态变为用户态,当程序要读取的数据已经完全存入到内核缓冲区后,程序会将数据由内核缓冲区读入到用户缓冲区,这个过程需要CPU 参与数据的读写。
  3. 程序使用write系统调用,系统由用户态切换为内核态,数据从用户缓冲区写入到网络缓冲区,这个过程需要cpu参数数据的读写。
  4. 系统由内核态切换到用户态,网络缓冲区的数据通过DMA的方式传输到存储缓冲区中

可以看到,普通的拷贝过程经历了四次内核态和用户态的切换(上下文切换),两次CPU从内存中进行数据的读写过程,这种拷贝过程相对来说比较消耗系统资源。

mmap优化

这是使用的系统调用方法,这种方式的IO原理是将用户缓冲区的内存地址和内核缓冲区的内存地址做一个映射,也就是系统在用户态可以直接读取并操作内核空间的数据。

在这里插入图片描述

图解说明:

  1. mmap系统调用首先会使用DMA的方式将磁盘数据读取到内核缓冲区,然后通过内存映射的方式,使用户缓冲区和内核缓冲区的内存地址为同一内存地址,也就是说不需要cpu再将数据从内核缓冲区复制到用户缓冲区。
  2. 当使用write系统调用的时候,cpu将内核缓冲区(等同于用户缓冲区)的数据直接写入到网络发送缓冲区(socket buffer),然后通过DMA的方式将数据传入到网卡驱动程序中准备发送。

综上,mmap内存映射这个方式减少了cpu的读写次数 但是用户态与内核态的切换次数依旧没有减少,还是4次。

sendFile优化

sendFile基本原理:数据不经过用户态,直接从内核缓冲区进入到Socket Buffer。由于与用户态无关,因此减少了一次上下文切换。

在这里插入图片描述
图解说明:

  1. sendFile系统调用会引起用户态到内核态的切换,从图中可知,这次的CPU copy对用户空间是不可见的,即用户空间无法看到或修改数据内容
  2. 从磁盘读取到内存是DMA方式,从内核读缓冲区读取到网络发送缓冲区,需要借助CPU copy,而从网络发送缓冲区到网卡中的缓冲区仍然是DMA方式

综上,这种方式有一次CPU 拷贝,2次用户态与内核态的切换。虽然比mmap有很大的进步,但是程序不能对数据进行修改,只能是单纯地数据传输。

在Linux 2.4版本中,对sendFile()函数做了优化,避免了从内核缓冲区拷贝数据到Socket Buffer的操作,直接拷贝到协议栈,从而再一次减少了数据拷贝。

在这里插入图片描述

虽然从kernel buffer->socket buffer有拷贝,但是拷贝的信息很少,可以忽略

零拷贝

零拷贝从操作系统角度是没有CPU 拷贝。零拷贝不仅能够带来更少的数据复制,还能带来其他的性能优势,比如更少的上下文切换,更少的CPU缓存伪共享以及无CPU校验和计算.

mmap和sendFile的区别:

  1. mmap适合小数据量的读写,sendFile适合大数据量的传输
  2. sendFile可以利用DMA方式,减少cpu拷贝,mmap则不能(必须从内核缓冲区拷贝到Socket缓冲区)

Channel中transferTo和transferFrom就是使用了零拷贝技术,因此传输会很快

发布了116 篇原创文章 · 获赞 23 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/zyxwvuuvwxyz/article/details/104112856