零拷贝(Zero-Copy)

一,数据的四次拷贝与四次上下文切换

很多应用程序在面临客户端请求时,可以等价为进行如下的系统调用:

  • File.read(file, buf, len);
  • Socket.send(socket, buf, len);

例如消息中间件 Kafka 就是这个应用场景,从磁盘中读取一批消息后原封不动地写入网卡(NIC,Network interface controller)进行发送。在没有任何优化技术使用的背景下,操作系统为此会进行 4 次数据拷贝,以及 4 次上下文切换,如下图所示:

如果没有优化,读取磁盘数据,再通过网卡传输的场景性能比较差:

4次copy

  • CPU 负责将数据从磁盘搬运到内核空间的 Page Cache 中
  • CPU 负责将数据从内核空间的 Page Cache 搬运到用户空间的缓冲区
  • CPU 负责将数据从用户空间的缓冲区搬运到内核空间的 Socket 缓冲区中
  • CPU 负责将数据从内核空间的 Socket 缓冲区搬运到的网络中

4次上下文切换

  • read 系统调用时:用户态切换到内核态
  • read 系统调用完毕:内核态切换回用户态
  • write 系统调用时:用户态切换到内核态
  • write 系统调用完毕:内核态切换回用户态

问题分析

  • CPU 全程负责内存内的数据拷贝还可以接受,因为效率还算可以接受,但是如果要全程负责内存与磁盘、网络的数据拷贝,这将难以接受,因为磁盘、网卡的速度远小于内存,内存又远远小于 CPU
  • 4 次 copy 太多了,4 次上下文切换也太频繁了

二,DMA技术

DMA,英文全称是Direct Memory Access,即直接内存访问。DMA本质上是一块主板上独立的芯片,允许外设设备和内存存储器之间直接进行IO数据传输,其过程不需要CPU的参与。我们一起来看下IO流程,DMA等忙做了什么事情。

1,用户应用进程调用read函数,向操作系统发起IO调用,进入阻塞状态,等待数据返回.
2,CPU收到指令后、对DMA控制器发起指令调度。
3,DMA收到IO请求后,将请求发送给磁盘:
做盘将数圳放入做盘控制缓冲区,并通知DMA
4,DMA将数据从微盘控制器缓冲区揭贝到内核缓冲区。
5,DMA向CPU发山数据读完的信号,把工作交换给CPU,由CPU负责将数据从内核缓冲区拷贝到用户缪冲区。
6,用户应用进程山内核态切换回用户态,解除阻寒状态

可以发现,DMA做的事情很清晰哦,它主要就是帮TCPU转发一下IO请求,以及将贝数据。

为什么需要它的?

主要就是效率,它帮助CPU做事情,这时候,CPU就可以闲下来大做别的事情,提高了CPU的利用效率。大白话解释就是,CPU老哥太忙太器啦,所以他找了个小弟《名叫DMA) ,替他完成一部分的拷贝工作,这样CPU老哥就能着手于做其他事情。

三,零拷贝技术

零拷贝技术是一个思想,指的是指计算机执行操作时,CPU 不需要先将数据从某处内存复制到另一个特定区域。

可见,零拷贝的特点是 CPU 不全程负责内存中的数据写入其他组件,CPU 仅仅起到管理的作用。但注意,零拷贝不是不进行拷贝,而是 CPU 不再全程负责数据拷贝时的搬运工作。如果数据本身不在内存中,那么必须先通过某种方式拷贝到内存中(这个过程 CPU 可以不参与),因为数据只有在内存中,才能被转移,才能被 CPU 直接读取计算。

零拷贝技术的具体实现方式有很多,例如:

  • mmap+write
  • sendfile
  • 带有DMA收集拷贝功能的sendfile

不同的零拷贝技术适用于不同的应用场景,下面依次进行分析。

1,mmap

mmap 的函数原型如下:

addr: 指定映射的虚拟内存地址
length:映射的长度
prot:映射内存的保护模式
fags: 指定映射的类型
fa:进行映射的文件句柄
offset:文件偏移量

前面一小节,零找贝相关的知识点回顾,我们介绍了虚拟内存,可以把内核空间和用户空间的虚拟地址映射到同一个物理地址,从而减少数据拷贝次数! mmap就是用了虚拟内存这个特点,它将内核中的读级冲区与用户空间的缓冲区进行映射,所有的IO都在内核中完成。

1,用户进程通过 mmap方法向操作系统内核发起IO调用,上下文从用户态切换为内核态。

2,CPU利用DMA控制器,把数据认硬盘小拷贝到内核缓冲区。

3,上下文从内核态切换回用户态,mmap方法返回。

4,用户进程通过 write 方法向操作系统内核发起IO调用,上下文从用户态切换为内核态。

5,CPU将内核缓冲区的数据拷贝到的socket缓冲区。

6,CPU利用DMA控制器,把数据从socket缓冲区拷贝到网卡,上下文从内核态切换回用户态,write调用返回。

可以发现, mmap+write 实现的零拷贝,I/0发生了4次用户空间与内核空间的上下文切换,以及3次数据拷贝。其中3次数据拷贝中,包括了2次DMA拷贝和1次CPU拷贝。
mmap 是将读缓冲区的地址和用户缓冲区的地址进行映射,内核缓冲区和应用缓冲区共享,所以节省了一次CPU拷贝并且用户进程内存是虚拟的,只是映射到内核的读缓冲区,可以节省一半的内存空间。

2,sendfile

sendfile 是Linux2.1内核版本后引入的一个系统调用函数,API如下:

out_fd:为待写入内容的文件描述符,一个socket描述符。

in_fd:为待读出内容的文件描述符,必须是真实的文件,不能是socket和管道。

offset: 指定从读入文件的哪个位置开始读,如果为NULL,表示文件的默认起始位置

count:指定在fdout和fdin之间传输的字节数。

sendfile表示在两个文件描述符之间传输数据,它是在操作系统内核中操作的,避免了数据从内核缓冲区和用户缓冲区之间的拷贝操作,因此可以使用它来实现零拷贝。

1,用户进程发起sendfile系统调用,上下文(切换1)从用户态转向内核态

2,DMA控制器,把数据从硬盘中拷贝到内核缓冲区。

3,CPU将读缓冲区中数据拷贝到socket缓冲区

4,DMA控制器,异步把数据从socket缓冲区拷贝到网卡,

5,上下文(切换2)从内核态切换回用户态,sendfile调用返回。

可以发现, sendfile 实现的零拷贝,I/O发生了2次用户空间与内核空间的上下文切换,以及3次数据拷贝。其中3次数据拷贝中,包括了2次DMA拷贝和1次CPU拷贝。那能不能把CPU拷贝的次数减少到0次呢? 有的,即带有DMA收集拷贝功能的sendfile !

3,带有DMA收集拷贝功能的sendfile

linux2.4版本之后,对 sendfile 做了优化升级,引入SG-DMA技术,其实就是对DMA拷贝加入了 scatter/gather 操作,它可以直接从内核空间缓冲区小将数据读收到网卡。使用这个特点搞零拷贝,即还可以多省去一次CPU拷贝。

1,用户进程发起sendfile系统调用,上下文(切换1)从用户态转向内核态

2,DMA控制器,把数据从硬盘中拷贝到内核缓冲区。

3,CPU把内核缓冲区中的文件描述符信息(包括内核缓冲区的内存地址和偏移量)发送到socket缓冲区

4,DMA控制器根据文件描述符信息,直接把数据从内核缓冲区拷贝到网卡

5,上下文(切换2)从内核态切换回用户态,sendfile调用返回。

可以发现, sendfile+DMA scatter/gather 实现的零拷贝,I/0发生了2次用户空间与内核空间的上下文切换,以及2次数据拷贝。其中2次数据拷贝都是包DMA拷贝。这就是真正的零拷贝(Zero-copy)技术,全程都没有通过CPU来搬运数据,所有的数据都是通过DMA来进行传输的。

三,java实现的零拷贝

Java Nio mmap的文持

javaNIO有一个 MappedByteBuffer 的类,可以用米实现内存映射。它的底层是调用了Linux内
核的mmap的API,Rocketmq这个开源项目就用到它。

Java NIO对sendfile的支持

FileChannel的 transferTo()/transferFrom() ,底层就是sendfile 系统调用函数。Kafka这个开源项目就用到它。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值